Java集合类
两耳不闻窗外事,一心只读圣贤书。
1.概述
Java集合类主要是Collection接口下的单列集合和Map接口下的K-V键值对类型的集合。
2.Collection
2.1 Collection 中的常用方法
contains(Object o)
contains方法的作用是查询当前集合中是否存在所传入的值。
查询的方式是查询传入对象的hashcode。
举例:
Collection coll = new ArrayList();
// 对比测试实例化一个对象的hashCode
Person per1 = new Person("Tom");
Person per2 = new Person("Tom");
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
// 对比测试一个字符串对象的hashCode
String jack1 = new String("Jack");
String jack2 = new String("Jack");
System.out.println(jack1.hashCode());
System.out.println(jack2.hashCode());
coll.add(new Person("Tom"));
coll.add(new String("Jack"));
// 返回结果为false,hashCode不同。
System.out.println(coll.contains(new Person("Tom")));
// 这里返回的结果是True,说明contains方法查询的是hashCode值。
System.out.println(coll.contains(new String("Jack")));
结论:String类已经重写了equals方法,因此最后的hashCode是相同的。(重写的比较的是值来定义hashcode)
但是普通的类用的Object默认的equals,直接比较的内存地址,因此对于用户自定义的类,使用contains方法的结果是false。
本质是调用传入对象的equals方法来比较查询。
如果需要调用contains方法时,传入的对象需要重写equals方法。
containsAll(Collection coll)
传入的集合中的数据是否都存在需要查询的集合中。
注意:只有全部都在才会返回true。
remove(Object o)
通过equals来进行判断是否存在元素,如果存在就进行删除,删除成功返回true。
删除失败返回false。
removeAll(Collection coll)
从当前集合中移除传入的集合中的元素。
移除的是两个集合的交集。
retainAll(Collection coll)
获取两个集合的交集,并修改当前的集合。
使当前集合中只存在两个集合共同存在的。
equals(Object o)
比较两个集合是否相同。
注意:ArrayList是有序的,因此比较时顺序会影响结果,如果顺序不同也会返回false。
hashCode()
返回hashCode值,如果没有重写就是返回内存地址。
如果重写了hashCode方法,则返回的结果自定义。
toArray()
返回相应类型的数组。
如果没有使用泛型,那么默认是Object[]型数组。
补充:如果将数组转换为集合?
通过Arrays.asList([对象数组]),来进行数组到集合的转换。
注意:
如果传入的是基本数据类型的数组(int、boolean等),那么会将传入的整个数组当成一个对象进行处理,只有转换为相应的包装类对象才能达到想要的结果。
List<int[]> ints = Arrays.asList(new int[]{
1, 2, 3});
// 虽然我们上面定义的数组含有多个元素,但是因为是基本数据类型,list中需要存放的是
// 对象数据类型,那么就会将整个数组当成一个对象进行处理,最后输出的结果:长度为1。
System.out.println(ints.size());
// 解决办法:将基本数据类型的数组转换为包装类对象进行传入 ==》 new Integer[]
// 通过包装类对象以后就能正常将传入的数组转换为list
List<Integer> integers = Arrays.asList(new Integer[]{
1, 2, 3});
System.out.println(integers.size());
iterator()
返回一个迭代器实例。
每次调用方法都会返回新的一个迭代器实例对象。
迭代器实例就是用于帮助我们遍历集合。
在Java设计模式中有详细的介绍。
注意:
Map中不是通过迭代器进行遍历,迭代器遍历主要引用与Collection中的集合。
-
hasNext()
判断是否含有写一个
-
next()
先指针下移,然后再输出元素
-
remove()
移除当前遍历到的元素,先next以后才能remove,如果没有next直接remove那么会爆出:IllegalStateException
原因:next开始是-1,那么无法删除,还有一种情况也会爆出这个异常,next以后进行了两次remove。
同理,remove是删除当前元素,删除以后指针并没有发生改变,如果在remove就会爆出异常。
集合的遍历除了迭代器以外,还有一种是forEach,这也是比较常用的一种方式。
语法:for([Object类型] [集合中具体的元素]:[集合]){}
本身也是通过迭代器实现。
不能通过修改遍历出的值,来修改集合对象中的值。
原因:
本身foreach是通过将集合中的值取出然后赋值到一个对象上,那么如果我们通过foreach来修改遍历出的值,是对本身集合对象是没有任何影响的。
forEach(Consumer action)
Collection类中默认是可以通过这个方法进行forEach遍历循环。
coll.forEach(System.out ::println);
2.2 List
Collection中的一个子类接口。
存储有序的、可重复的数据。
2.2.1 概述
- ArrayList
- LinkedList
- Vector
2.2.2 ArrayList、LinkedList、Vector的异同
相同点
- 三个类都实现了List接口
- 存储的数据都是有序、可重复的
不同点
-
ArrayList是作为List主要实现类(首选)
线程不安全,效率高
底层是通过Objec[]数组存储,动态扩展
-
Vector是List接口的古老实现类
推出Java就已经存在的一个类(V 1.0)出现,List(V 1.2)才出现。
线程安全,效率低
底层是通过Objec[]数组存储,动态扩展
-
LinkedList通过链表实现和另外两种不同实现方式不同
双向链表实现
线程不安全
对于增加/删除效率比数组实现的List类效率更高,同理查找没有数组实现的List类高
2.3 List中的实现类源码分析
2.3.1 ArrayList
构造方法,无参构造默认大小为10,但是不会再实例化对象时创建数组,而是创建一个空数组,当进行第一次执行add()方法时才会进行创建。
扩容为1.5倍加1。
-
Object[] elementData实现,动态扩容
transient Object[] elementData;
-
new实例对象时,可以指定大小,如果没有指定,那么默认创建大小为10
/** * Constructs an empty list with an initial capacity of ten. */ public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; }
常用方法
下面演示add()方法。
其他方法类似在进行操作之前都会进行一些数组上面的判断。
add()
先判断,再添加。数组长度不够,扩容,每次扩容原先数组的1.5倍。
不是无止境的扩容,最大值为int类型最大值。
结论:建议在创建ArrayList时指定大小。
源码
public boolean add(E e) {
// 添加元素前会进行容量判断如果容量够进行添加
// 如果不够会进行扩容
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
// 扩容判断
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 判断当加入这个元素以后是否会超过数组的长度
if (minCapacity - elementData.length > 0)
// 如果加入元素超过了数组的长度,进行扩容
grow(minCapacity);
}
// 进行扩容
private void grow(int minCapacity) {
int oldCapacity = element