- 概念:对象的容器,定义了对多个对象进项操作的的常用方法。可实现数组的功能。
- 和数组的区别:
- 数组长度固定,集合长度不固定。
- 数组可以存储基本类型和引用类型,集合只能存储引用类型。
- 位置: java.util.*;
Collection体系集合
Coolection父接口
- 特点:代表一组任意类型的对象,无序、无下标、不能重复。
-
方法:
boolean add(Object obj) //添加一个对象。
boolean addAll(Collection c) //讲一个集合中的所有对象添加到此集合中。
void clear() //清空此集合中的所有对象。
boolean contains(Object o) //检查此集合中是否包含o对象。
boolean equals(Object o) //比较此集合是否与指定对象相等。
boolean isEmpty() //判断此集合是否为空。
boolean remove(Object o) //在此集合中移除o对象。
int size() //返回此集合中的元素个数。
Object[] toArray() //姜此集合转换成数组。
public class Demo01 {
public static void main(String[] args) {
Collection collection =new ArrayList();
//1.添加元素
collection.add("语文");
collection.add("数学");
collection.add("英语");
collection.add("物理");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
//2.删除元素
collection.remove("数学");
System.out.println("元素个数:"+collection.size());
System.out.println(collection);
//3.遍历元素
System.out.println("------使用增强for------");
for (Object o:collection) {
System.out.println(o);
}
System.out.println("-----使用迭代器------");
Iterator iterator=collection.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
//迭代过程中不能使用collection的删除方法
//collection.remove();引发错误:并发修改异常
//iterator.remove();//应使用迭代器的方法
}
//4.判断
System.out.println(collection.isEmpty());//false
System.out.println(collection.contains("英语"));//true
}
}
List接口
- 特点:有序、有下标、元素可以重复。
-
方法:
void add(int index,Object o) //在index位置插入对象o。
boolean addAll(index,Collection c) //将一个集合中的元素添加到此集合中的index位置。
Object get(int index) //返回集合中指定位置的元素。
List subList(int fromIndex,int toIndex) //返回fromIndex和toIndex之间的集合元素。
public class Demo02 {
public static void main(String[] args) {
List list=new ArrayList();
//1.添加
list.add("苹果");
list.add("香蕉");
list.add(0,"梨");
List list1=new ArrayList();
list1.add("芒果");
list.addAll(2,list1);
System.out.println("元素个数为"+" "+list.size());//元素个数为 4
System.out.println(list);//[梨, 苹果, 芒果, 香蕉]
//2.删除
// list.remove(1);
// System.out.println(list);
//3.遍历元素
System.out.println("----for循环遍历---");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));//返回指定位置的元素
}
System.out.println("----增强for遍历---");
for (Object o:list
) {
System.out.println(o);
}
System.out.println("----迭代器遍历---");
Iterator it=list.iterator();
while (it.hasNext())
System.out.println(it.next());
System.out.println("------使用列表迭代器,listIterator可以双向遍历,添加、删除及修改元素。----");
ListIterator listIterator=list.listIterator();
while (listIterator.hasNext())
{
System.out.println(listIterator.next());
}
System.out.println("------从后往前遍历-----");
while (listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
//4.判断
System.out.println(list.isEmpty());//false
System.out.println(list.contains("香蕉"));//true
//5.获取位置
System.out.println(list.indexOf("苹果"));
//6.补充方法subList,返回子集合,含头不含尾,左闭右开
List list2=list.subList(1, 3);
System.out.println(list2);//[苹果, 芒果]
//注意:当类型为数字类型时,add(5)会自动封箱
//使用删除方法remove(5)只能删除下标为5的元素
//要删除5这个元素要使用强转换remove(Object(5))或remove(new Integer(20))
}
}
Lsit实现类
- ArrayList 【重点】
- 数组结构实现,必须要连续空间,查询快、增删慢
- jdk1.2版本,运行效率块、线程不安全
- Vector
- 数组结构实现,查询快、增删慢
- jdk1.0版本,运行效率慢,线程安全
- LinkedList
- 双向链表结构实现,无需连续空间,增删快,查询慢
ArrayList
public class Demo3 {
public static void main(String[] args) {
ArrayList arrayList=new ArrayList<>();
//1.添加元素
Student s1=new Student("小明", 21);
Student s2=new Student("小红", 22);
Student s3=new Student("张三", 21);
arrayList.add(s1);
arrayList.add(s2);
arrayList.add(s3);
System.out.println("元素个数:"+arrayList.size());
System.out.println(arrayList.toString());
//2.删除元素
arrayList.remove(s1);
//arrayList.remove(new Student("张三", 21));
//注:这样可以删除吗(不可以)?显然这是两个不同的对象。
//假如两个对象属性相同便认为其是同一对象,那么如何修改代码?
//3.遍历元素
//3.1使用迭代器
Iterator iterator=arrayList.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
//3.2使用列表迭代器
ListIterator listIterator=arrayList.listIterator();
//从前往后遍历
while(listIterator.hasNext()) {
System.out.println(listIterator.next());
}
//从后往前遍历
while(listIterator.hasPrevious()) {
System.out.println(listIterator.previous());
}
//4.判断
System.out.println(arrayList.isEmpty());
//System.out.println(arrayList.contains(new Student("小红", 22)));
//注:与上文相同的问题。
//5.查找
System.out.println(arrayList.indexOf(s1));
}
}
注:Object里的equals(this==obj)用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写equals方法:
COPY@Override
public boolean equals(Object obj) {
//1.是否为同一对象
if (this==obj) {
return true;
}
//2.判断是否为空
if (obj==null) {
return false;
}
//3.判断是否是Student类型
if (obj instanceof Student) {
Student student=(Student) obj;
//4.比较属性
if(this.name.equals(student.getName())&&this.age==student.age) {
return true;
}
}
//不满足,返回false
return false;
}
原码分析
DEFAULT_CAPACITY = 10; //默认容量
//注意:如果没有向集合中添加任何元素时,容量0,添加一个后,容量为10
//每次扩容是原来的1.5倍
elementData存放元素的数组
size 实际元素个数
Vector
public class Demo4 {
public static void main(String[] args) {
Vector vector=new Vector<>();
//1.添加数据
vector.add("苹果");
vector.add("香蕉");
vector.add("梨");
System.out.println("元素个数:"+vector.size());
//2.删除数据
/*
* vector.remove(0); vector.remove("苹果");
*/
//3.遍历
//使用枚举器
Enumeration enumeration=vector.elements();
while (enumeration.hasMoreElements()) {
String s = (String) enumeration.nextElement();
System.out.println(s);
}
//4.判断
System.out.println(vector.isEmpty());
System.out.println(vector.contains("苹果"));
//5. Vector其他方法
//firstElement() lastElement() ElementAt();
}
}
LinkedList
/**
* LinkedList的用法
* 存储结构:双向链表
* 1.添加元素
* 2.删除元素
* 3.遍历
* 4.判断
*/
public class Demo5 {
public static void main(String[] args) {
LinkedList linkedList=new LinkedList<>();
Student s1=new Student("小明", 21);
Student s2=new Student("小红", 22);
Student s3=new Student("张三", 21);
//1.添加元素
linkedList.add(s1);
linkedList.add(s2);
linkedList.add(s3);
linkedList.add(s3);
System.out.println("元素个数:"+linkedList.size());
System.out.println(linkedList.toString());
//2.删除元素
/*
* linkedList.remove(new Student("张三", 21));
* System.out.println(linkedList.toString());
*/
//3.遍历
//3.1 使用for
for(int i=0;i<linkedList.size();++i) {
System.out.println(linkedList.get(i));
}
//3.2 使用增强for
for(Object object:linkedList) {
Student student=(Student) object;
System.out.println(student.toString());
}
//3.3 使用迭代器
Iterator iterator =linkedList.iterator();
while (iterator.hasNext()) {
Student student = (Student) iterator.next();
System.out.println(student.toString());
}
//3.4 使用列表迭代器(略)
//4. 判断
System.out.println(linkedList.contains(s1));
System.out.println(linkedList.isEmpty());
System.out.println(linkedList.indexOf(s3));
}
}
ArrayList和LinkedList区别
- ArrayList:必须开辟连续空间,查询快,增删慢。
- LinkedList:无需开辟连续空间,查询慢,增删快。
泛型
- 本质是参数化类型,把类型作为参数传递
- 常见形式有泛型类、泛型接口、泛型方法
- 语法 T成为类型占位符,表示一种引用类型,可以写多个逗号隔开
- 好处 1. 提高代码重用性 2. 防止类型转换异常,提高代码安全性
泛型类
/**
* 泛型类
* @param <T>
*/
public class MyGeneric<T> {
T t;
public void show(T t){
System.out.println(t);
}
public T getT(){
return t;
}
}
/** * 注意: * 1.泛型只能使用引用类型 * 2.不同泛型类型的对象不能相互赋值 */public class Application { public static void main(String[] args) { MyGeneric<String> myGeneric=new MyGeneric<String>(); myGeneric.t="hello"; myGeneric.show("myGeneric");//myGeneric String string = myGeneric.getT(); System.out.println(string);//hello MyGeneric<Integer> myGeneric1=new MyGeneric<Integer>(); myGeneric1.t=100; myGeneric1.show(200);//200 System.out.println(myGeneric1.getT());//100 }}
泛型接口
/** * 语法:接口名<T> * 注意:不能创建泛型静态常量 * 泛型接口 */public interface MyInterface<T> { String s="hello"; T server(T t);}
/** * 实现接口时确定泛型类 */public class MyInterfaceImpl implements MyInterface<String>{ @Override public String server(String s) { System.out.println(s); return s; }}
/** * 实现接口时不确定泛型类 */public class MyInterfaceImpl2<T> implements MyInterface<T> { @Override public T server(T t) { System.out.println(t); return t; }}
public class TestInterface { public static void main(String[] args) { MyInterfaceImpl myInterface=new MyInterfaceImpl(); myInterface.server("haha"); MyInterfaceImpl2<String> myInterfaceImpl2=new MyInterfaceImpl2<String>(); myInterfaceImpl2.server("xxxx"); }}
泛型方法
/** * 泛型方法 * 语法:<T> 返回类型 */public class MyGenericMethod { public <T> String get(T t){ System.out.println(t); return (String) t; }}
public class TestMethod { public static void main(String[] args) { MyGenericMethod myGenericMethod=new MyGenericMethod(); System.out.println(myGenericMethod.get("hello")); }}
泛型集合
- 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
- 特点:
- 编译时即可检查,而非运行时抛出异常。
- 访问时,不必类型转换(拆箱)。
- 不同泛型指尖引用不能相互赋值,泛型不存在多态。
之前我们在创建LinkedList类型对象的时候并没有使用泛型,但是进到它的源码中会发现:
public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, java.io.Serializable{//略}
它是一个泛型类,而之前使用的时候并没有传递,说明java语法是允许的,这个时候传递的类型是Object类,虽然它是所有类的父类,可以存储任意的类型,但是在遍历、获取元素时需要原来的类型就要进行强制转换。这个时候就会出现一些问题,假如往链表里存储了许多不同类型的数据,在强转的时候就要判断每一个原来的类型,这样就很容易出现错误。
Set接口
- 特点:无序、无下标、元素不可重复
- 方法:全部继承自Collection中的方法
- 增、删、遍历、判断与collection一致
Set实现类
HashSet(重点)
- 基于HashCode计算元素存放位置。
- 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && name.equals(person.name); } @Override public int hashCode() { return Objects.hash(name, age); }}
/** * HashSet集合的使用 * 存储结构:哈希表(数组+链表+红黑树) * 1.添加元素 * 2.删除元素 * 3.遍历 * 4.判断 */public class Demo01 { public static void main(String[] args) { HashSet<Person> hashSet = new HashSet(); Person p1 = new Person("张三", 20); Person p2 = new Person("李四", 22); Person p3 = new Person("王五", 25); //1.添加元素 hashSet.add(p1); hashSet.add(p2); hashSet.add(p3); //重复,添加失败 hashSet.add(p3); //直接new一个相同属性的对象,依然会被添加,不难理解。 //假如相同属性便认为是同一个对象,怎么修改?(重写hashCode和equals方法) System.out.println("元素个数为:"+hashSet.size()); System.out.println(hashSet.toString()); //2.删除元素// hashSet.remove(p1);// System.out.println("元素个数为:"+hashSet.size());// System.out.println(hashSet.toString()); //3.遍历元素 //增强for for (Person p:hashSet) { System.out.println(p); } //迭代器 System.out.println("-----------"); Iterator iterator=hashSet.iterator(); while (iterator.hasNext()) System.out.println(iterator.next()); //4.判断 System.out.println(hashSet.isEmpty()); System.out.println(hashSet.contains(p1)); System.out.println(hashSet.contains(new Person("王五", 25)));//重写后返回true }}
注:hashSet存储过程:
- 根据hashCode计算保存的位置,如果位置为空,则直接保存,否则执行第二步。
- 执行equals方法,如果方法返回true,则认为是重复,拒绝存储,否则形成链表。
存储过程实际上就是重复依据,要实现“注”里的问题,可以重写hashCode和equals代码:
可以选择自动重写方法如上,还可选择手动重写方法实现“注”里面的问题
@Overridepublic int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result;}@Overridepublic boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true;}
hashCode方法里为什么要使用31这个数字大概有两个原因:
- 31是一个质数,这样的数字在计算时可以尽量减少散列冲突。
- 可以提高执行效率,因为31*i=(i<<5)-i,31乘以一个数可以转换成移位操作,这样能快一点;但是也有网上一些人对这两点提出质疑。
TreeSet
- 基于排序顺序实现不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过CompareTo方法确定是否为重复元素。
/** * 使用TreeSet保存数据 * 存储结构:红黑树 * 要求:元素类必须实现Comparable接口,compareTo方法返回0,认为是重复元素 */public class Demo02 { public static void main(String[] args) { TreeSet<Person> persons=new TreeSet<Person>(); Person p1 = new Person("张三", 20); Person p2 = new Person("李四", 22); Person p3 = new Person("王五", 25); //1.添加元素 persons.add(p1); persons.add(p2); persons.add(p3); persons.add(p1); //注:直接添加会报类型转换错误,需要实现Comparable接口 System.out.println(persons.toString()); //2.删除元素// persons.remove(p1);// persons.remove(new Person("王五", 25));// System.out.println(persons.toString()); //3.遍历(略) //4.判断 System.out.println(persons.contains(new Person("王五", 25))); }}
public class Person implements Comparable<Person>{ @Override public int compareTo(Person p) { int n1=this.getName().compareTo(p.getName()); int n2=this.getAge()-p.getAge(); return n1==0?n2:n1; }}
除了实现Comparable接口里的比较方法,TreeSet也提供了一个带比较器Comparator的构造方法,使用匿名内部类来实现它:
public class Demo03 { public static void main(String[] args) { TreeSet<Person> persons=new TreeSet(new Comparator<Person>() { @Override public int compare(Person p1, Person p2) { int n1 = p1.getName().compareTo(p2.getName()); int n2 = p1.getAge() - p2.getAge(); return n1 == 0 ? n2 : n1; } }); Person p1 = new Person("张三", 20); Person p2 = new Person("李四", 22); Person p3 = new Person("王五", 25); //1.添加元素 persons.add(p1); persons.add(p2); persons.add(p3); persons.add(p1); System.out.println(persons.toString()); }}
Map集合概述
-
特点:存储一对数据(Key-Value),无序、无下标,键不可重复。
-
方法:
-
V put(K key,V value)
//将对象存入到集合中,关联键值。key重复则覆盖原值。 -
Object get(Object key)
//根据键获取相应的值。 -
Set<K>
//返回所有的key -
Collection<V> values()
//返回包含所有值的Collection集合。 -
Set<Map.Entry<K,V>>
//键值匹配的set集合
-
/** * map接口的使用 */public class Demo01 { public static void main(String[] args) { Map<String,Integer> map=new HashMap(); //添加元素 map.put("张三",25); map.put("李四",23); map.put("王五",18); System.out.println(map.size()); System.out.println(map.toString()); //删除元素// map.remove("张三");// map.remove("李四",23);// System.out.println(map.toString()); //遍历 //3.1 使用keySet();// Set<String> strings = map.keySet();// for (String string :strings) {// System.out.println(string+"-----"+map.get(string));// } for (String string :map.keySet()) { System.out.println(string+"-----"+map.get(string)); } System.out.println("-------"); //3.2 使用entrySet();效率较高 for(Map.Entry<String, Integer> entry:map.entrySet())// System.out.println(entry);// //李四=23// //张三=25// //王五=18 System.out.println(entry.getKey()+"----"+entry.getValue()); }}
Map集合的实现类
HashMap【重点】
- JDK1.2版本,线程不安全,运行效率快;允许用null作为key或是value。
/** * hashMap的使用 * 存储结构:哈希表(数组+链表+红黑树) */public class Demo02 { public static void main(String[] args) { HashMap<Person, String> hashMap = new HashMap<>(); Person p1 = new Person("张三", 20); Person p2 = new Person("李四", 22); Person p3 = new Person("王五", 25); hashMap.put(p1,"深圳"); hashMap.put(p2,"上海"); hashMap.put(p3,"湖北"); System.out.println(hashMap.toString()); //添加失败,但会更新值 hashMap.put(p3,"湖北"); //添加成功,不过两个属性一模一样; //注:假如相同属性便认为是同一个对象,怎么修改?(重写hashCode和equals方法) //hashMap.put(new Person("王五", 25),"湖北"); //System.out.println(hashMap.toString()); //2.删除元素// hashMap.remove(p1);// System.out.println(hashMap.toString()); //3.遍历 System.out.println("---使用keySet遍历----"); for (Person p:hashMap.keySet()) System.out.println(p+"-------"+hashMap.get(p)); System.out.println("----使用EntrySet遍历-----"); for (Map.Entry<Person,String> entry:hashMap.entrySet()) System.out.println(entry.getKey()+"---------"+entry.getValue()); }}
重写hashCode和equals方法可以判断元素是否重复
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && name.equals(person.name); } @Override public int hashCode() { return Objects.hash(name, age); }
Hashtable
-
JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value。
-
初始容量11,加载因子0.75。
这个集合在开发过程中已经不用了,稍微了解即可。
Properties
- Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
它继承了Hashtable的方法,与流关系密切,此处不详解。
TreeMap
- 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
/** * TreeMap的使用 * 存储结构:红黑树 */public class Demo03 { public static void main(String[] args) { TreeMap<Person, String> treeMap=new TreeMap<Person, String>(); Person p1 = new Person("张三", 20); Person p2 = new Person("李四", 22); Person p3 = new Person("王五", 25); //1.添加元素 treeMap.put(p1,"深圳"); treeMap.put(p2,"上海"); treeMap.put(p3,"湖北"); //不能直接打印,需要实现Comparable接口,因为红黑树需要比较大小 System.out.println(treeMap.toString()); //2.删除元素// treeMap.remove(new Person("王五", 25));// System.out.println(treeMap.toString()); //3.遍历 //3.1 使用keySet() for (Person key : treeMap.keySet()) { System.out.println(key+" "+treeMap.get(key)); } //3.2 使用entrySet() for (Map.Entry<Person, String> entry : treeMap.entrySet()) { System.out.println(entry.getKey()+" "+entry.getValue()); } //4.判断 System.out.println(treeMap.containsKey(p1)); System.out.println(treeMap.isEmpty()); }}
Collection工具集
概念:集合工具类,定义了除了存取以外的集合常用方法
-
int i = Collections.binarySearch(list, x);
直接二分查找,成功返回索引 -
public static void reverse(List<?> list)
反转集合中元素的顺序 -
public static void shuffle(List<?> list)
随机重置集合元素的顺序 -
public static void sort(List<T> list)
升序排序(元素类型必须实现Comparable接口) -
Collections.copy(dest,src)
复制,dest目标数组,src源数组
补充:
// list转成数组Integer[] arr = list.toArray(new Integer[10]);sout(arr.length);sout(Array.toString(arr));// 数组转成集合// 此时为受限集合,不能 添加和删除!String[] name = {"张三","李四","王五"};List<String> list2 = Arrays.asList(names);// 把基本类型数组转为集合时,需要修改为包装类Integer[] nums = {100, 200, 300, 400, 500};List<Integer> list3 = Arrays.asList(nums);