首先集合类的产生是由于数组存储元素时是定长的,为了解决数组定长问题,Java提供了动态对象数组,用来存储数据。
其次集合类中有两个核心的接口:Collection接口和Map接口
Collection接口主要是对单个对象进行保存处理,而Map接口主要是对一对对象进行保存处理
对于Collection接口来说,该接口有List子接口和Set子接口。List接口允许数据重复,而Set接口不允许数据重复。
对于List接口来说,该接口有ArrayList子类、Vector子类、LinkedList子类。
一、对于ArrayList、Vector、LinkedList这几个子类的关系与区别:
- 这几个类的都是List接口下的常用子类,其中ArrayList和Vector都是基于数组实现的,而LinkedList是基于双向链表实现的
- 从初始化、扩容、线程安全三方面对比ArrayList与Vector
2.1初始化
ArrayList采用懒加载策略(第一次添加元素时才初始化内部数组,默认初始化大小为10)
Vector在实例化对象时就初始化内部数组,大小也为10
2.2扩容
ArrayList扩容为原先数组大小的1.5倍
Vector扩容为原先数组大小的2倍
2.3线程安全
ArrayList采用异步处理,线程不安全,性能较高
Vector采用同步处理,线程安全,性能较低
3. LinkedList采用异步处理,线程不安全
二、这三个常用子类的适用场景
ArrayList适用于频繁查找元素的场景
Vector在实现线程安全时,由于是采用synchronized修饰增删改查方法,锁的粒度太粗(将当前对象锁住,读读都是互斥的),即使要用性能安全的集合,也不推荐使用Vector
LinkedList适用于频繁在任意位置插入和删除元素的场景
对于set接口来说,该接口有HashSet子类和TreeSet子类【两个常用子类】。HashSet存储元素时是无序存储,而TreeSet存储元素时是有序存储
对于HashSet来说,底层实现是HashMap;而对于TreeSet来说,底层实现是TreeMap
Set中存储的元素其实是存到Map的key中的
对于有序还是无序存储来说,这里的序指的是存储该类的大小关系,也就是内部或外部排序返回的大小关系
自定义的类要想存储在TreeSet中,要么该类实现了内部排序(如String、Integer),要么通过TreeSet的构造方法传入该类的比较器(实现了外部排序Comparator接口)
值得注意的是,TreeSet优先使用外部比较器
对于Map接口来说,该接口有HashMap、Hashtable、TreeMap和ConcurrentHashMap四个常用子类
HashMap、Hashtable、TreeMap的关系与区别:
- 这几个类都是Map接口的常用子类,其中
对于HashMap来说,在JDK1.8之后底层采用哈希表+红黑树实现,而在1.8之前单纯是采用哈希表实现;
TreeMap底层采用红黑树实现;
Hashtable底层采用哈希表实现;
2. 从线程安全方面来说
HashMap和TreeMap采用异步处理,线程不安全,性能较高;
Hashtable使用synchronized同步方法,线程安全,性能较低(给整个哈希表上锁,锁粒度太粗)
3. 从是否允许存储null来说
HashMap允许key和value都为空,但key只允许一个为null,value任意
TreeMap只允许value为null,不允许key为null
Hashtable不允许key和value为null
对于ConcurrentHashMap来说,
在JDK1.7时,其底层是采用segment+哈希表实现的;segment初始化为16后无法扩容,扩容只发生在每个小的哈希表中。其实现线程安全主要是使用lock体系来保证的【因为segment是ReentrantLock的子类】,整张表有16把锁,锁的对象是每个segment,支持的并发线程数是16;
而在JDK1.8时,其底层跟HashMap类似,也是采用哈希表+红黑树实现的,不再采用segment;主要采用synchronized+CAS同步代码块来保证线程安全,锁的对象为哈希表的每个数组元素(锁的粒度更细,锁的个数也更多,而且锁的个数会随着哈希表的扩容而增加)