目录
集合类是Java数据结构的实现,是java.util包中的重要内容 。集合将Java中使用频率极高的一些基础类进行封装和增强后以一个类的形式提供,不同的集合类有不同的功能和特点、适用场景 。
集合中容纳的对象实际上只是对象名,也就是指向地址的指针,如果想在集合类中使用简单的数据类型,又想保留集合的灵活性,可以将简单数据类型的数据变为该数据类型的对象,再放入集合中处理。集合类容纳的对象都是Object类的实例,如果将一个对象置于集合类中,它的类信息就会丢失,所以集合中容纳的只是指向Object类对象的指针。因为Object类是Java的跟类,所以这样设计可以保证集合中能够存放任何类而不受限制,缺点是使用集合前必须对它重新造型,执行效率也会降低。
集合的分类
Map接口和Collection接口是所有集合框架的父类接口。继承了Collection的接口有Queue、List、Set,其中Set又被SortedSet继承,继承了Map的接口有SortedMap和AbstractMap。在集合的学习中需要掌握的抽象类有HashMap、ArrayList、LinkedList、HashSet、HashTable、Stack、TreeSet、TreeMap等。以上提到的接口和抽象类之间的关系如下图所示:
图片引用自路人张面试手册V3
集合的特点和优势
1、性能高:集合对基本类集,如动态数组、链表、树结构、散列表的实现是高效率的,对于成熟高效的API,一般没有人去改动。
2、不同类型的集合可以用相同的方式和高度互操作方式工作。
3、集合类容易扩展和修改,能够自由满足数据结构需求。
使用Java提供的集合类可以降低编程难度,同时Java的集合类经过了各位前辈的不断的改进和总结,具有较高的质量和运行速度,使用这些集合类提供的数据结构,可以避免重复”造轮子“,转而将精力放在提示程序上。并且借助泛型,只需要了解各个类的使用方法,就可以将其应用到很多数据类型中,无需再为了每一种数据类型去学习新的API。
Collection和Collections的区别
Collection是单列集合的根接口,提供了对集合对象进行基本操作的通用接口方法,Java中所有的集合都由Collection触发。Collection常用的方法如下:
add: 在集合末尾添加元素,返回值为boolean
clear:移除本类中的所有元素
contains:判断集合中是否包含指定元素
equals:判断元素是否相等
isEmpty:判断集合是否为空
remove:移除与指定元素的值相等的元素并返回true
size:返回集合的元素个数
toArray:返回包含本集所有元素的数组
hashCode:返回元素的哈希值
可以总结为(没有尾巴的access h和 没有头的first)
Collections是一个包装类,包含有关集合操作的静态多态方法,比如排序、搜索以及序列化的易操作。Collections更多像一个工具类,服务一Collection框架,不能被实例化。常用的方法如下:
排序(Sort): 根据元素的自然顺序对指定列表按照升序进行排列
反转(Reverse):根据元素的自然顺序对指定列表按降序排列
替换所有的元素(fill)
拷贝(copy)
返回Collections中最小元素(min)、返回Collections中最大元素(max)
lastIndexOfSubList:返回指定源列表中最后一次出现指定目标列表的起始位置
IndexOfSubList:返回指定源列表中第一次出现指定目标列表的起始位置
Rotate:根据指定的距离循环移动指定列表中的元素
快速失败和安全失败机制
快速失败:
快速失败机制是Java集合框架的一种错误检测机制。多线程同时对集合中的内容进行修改或再单线程中使用增强for循环一边遍历一边修改集合元素,会抛出ConcurrentModificationException异常。
例如单线程的情况下,调用在增强for循环中调用remove方法,查看ArrayList的源码,发现在AbstractList中定义了变量modCount和在ArrayList中的内部类Itr中定义了变量expectedModCount。直接调用集合的remove方法时由于两个变量的初始值,会导致两个变量并不相等,因此抛出异常。而迭代器调用的remove方法中直接定义了modCount=expectedModCount,因此使用迭代器的remove()方法不会出现这种问题。
但是多线程的情况下使用迭代器调用remove()方法,依旧会报异常,这是由于定义在ArrayList内部的expectedModCount变量属于线程,而modCount定义在AbstractList中,是共享的变量,所以多个线程只更新modCount和各自的expectedModCount,就会判断两者不相等,因此报异常。
安全失败
采用安全失败机制的集合容器,遍历时先复制原有集合的内容,然后在拷贝的集合上进行遍历。所以迭代器无法检测到遍历过程中对原集合的修改,因此就不会抛出异常。缺点是迭代器也无法访问到修改后的内容。
迭代器
Iterator是Java迭代器最简单的实现,它不是一个集合,是一种访问集合的方法,提供了遍历任何Collection的接口。
Comparable和Comparator的区别
Comparable
Comparable接口出自java.lang包,实现了Comparable的类可以和自己作比较,如果要和其他实现了Comparable的接口类进行比较,需要使用compareTo(Object obj)方法。compareTo方法的返回值为int,有三种情况:
比较者大于被比较者:返回正整数;
两者相等返回0;
比较者小于被比较者:返回负整数。
Comparator
Comparator出自java.util包,用来排序的方法是compare,返回值与compareTo类似。
两者的区别:
实现了Comparable的包装类,直接调用Collections.sort()进行排序,但是如果对自然排序的结果不满意,而又不能修改源码,使用comparator更合适,可以避免添加额外代码与目标类耦合,也可以自定义排序规则,从灵活性和扩展性讲比comparable更优。
线程安全的集合类
vector:相当于有同步机制的ArrayList
stack:栈
HashTable
enumration:枚举
这些线程安全的集合类都是通过Synchroized来保证线程安全的。像Vector和HashTable已经被官方标注为即将废弃,因此不建议使用。
针对非线程安全的集合类,比如ArrayList,如果想要在多线程的情况下安全使用,可以利用Synchronized或ReentrantLock进行加锁。