二、集合
1、集合的基本接口
- Collection:代表一组对象,每一个对象都是其子元素
- Set:是一个不包含重复元素的Collection
- List:是一个有序的Collection,可存储重复元素。
- Map:是通过键值对一一映射进行存储的接口,Key不可重复,value可重复
2、ArrayList的理解
- ArrayList是List接口的一个实现类,其底层是transient Object[]数组;通过索引可以直接访问数据,时间复杂度是O(1);若是增/删除第i个数据,那么时间复杂度就是O(n-i)
- 扩容机制:当创建ArrayList时若没有指定数组大小,默认的容量是10;在添加数据时,若此时元素个数大于容量,就会触发扩容机制,新集合的容量是旧集合容量的1.5倍
- 线程问题:ArrayList是线程不安全的,在执行add方法的时候,首先会检查一下容量是否够用,若够用,通过当前索引给数组赋值,然后再对索引进行++操作,这是个非原子性操作,所以在并发环境下,会出现问题
- Vector区别:protected object[] 数组,扩容2倍,因为加了Synchroized关键字,所以是线程安全,但是效率较低
3、说说对LinkedList的理解
- LinkedList也是List的实现类之一,其底层是双向链表,内部有一个next指针指向下一个节点,和一个pre指向上一个节点;因此对其作查询操作的是否时间复杂度为O(n),需要从头遍历;但是做增/删操作的时候,时间复杂度为O(1)
- 线程问题:LinkedList也是线程非安全
4、HashSet的理解
HashSet是Set集合的一个实现类,底层是使用HashMap的key,初始化的容量是16,负载因子是0.75,扩容机制是变为原来的2倍
存储机制:hashset存储的顺序不是存入的顺序,而是依照哈希值来对数据进行存储;当存储某一个值的时候,会先计算这个对象的hash值,通过hash值找到这个对象要存储的位置,若此位置没有对象直接存,若有需要通过equals方法判断是否是同一个对象,若返回true则不允许添加,若返回false则允许添加
5、HashMap的理解
- HashMap是Map的一个实现类,底层是数组+链表+红黑树组成;所有的数据都是一个Node进行封装,包括hash值、key、value和next指针。hash值是通过计算key调用hashcode方法,再进行一些计算得到。
- 扩容机制:HashMap的初始化容量是16,当使用量为容量的0.75倍时,便会进行扩充,扩充为原来的两倍
- 存储机制:但在put一个键值对时,通过key可以计算出hash值,计算得到的hash值作为数组的索引,然后判断此位置是否有数据:
- 若没有就直接添加
- 若有,用equals方法判断该元素的key与新的key是否是同一个元素,若是,则直接用新的value替换旧的value,若不是直接添加,并计算链表长度,若长度大于8会触发红黑树方式存储。单元数个数小于等于6(防止频繁转换)是会从红黑树转变为链表形式
6、集合类线程不安全
6.1 ArrayList不安全之写时复制
当多个线程对ArrayList进行写操作的时候,可能会引发ConcurrentModificationException;
解决的办法:
- 把ArrayList改为Vector
- 改为Collections.synchronizedList
- CopyOnWriteArrayList
- 原理: 在一个容器添加元素的时候,先将当前容器进行复制,然后新的容器中添加元素,添加完元素之后,再将原容器的引用指向新的容器。这样做的好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁
6.2 Set不安全
解决:
- Collections.sychnorizedSer();
- CopyOnWriteArraySet
6.3 Map不安全
解决:
- HashTable
- Collections.sychnorizedMap
- ConCurrentMap<>()
1.7采用的是数组加链表的结构,与HashMap不同的是,ConcurrentHashMap中数组设计分为大数组是一个Segment与小数组HashEntry,每一个Segment实例包含若干个HashEntry对象。Segment本身是基于ReentrantLock重入锁实现的加锁和释放锁操作,可以对容器中的一部分数据进行加锁,因此多线程访问容器中其他数据时不会存在竞争关系,提高访问率。
1.8中是放弃了segment,而是采用node数组+链表+红黑树的方式进行优化,在线程安全方面使用CAS和sychnorized来进行保证;
7、红黑树
红黑树是在二叉树的基础上提出的,因为二叉树可能出现倾斜的现象(两边树高的差异较大),导致在查询时可能出现时间复杂度读为O(n)的情况,因此在此基础上引入了红黑树。红黑树理论上平衡树,是通过左旋与右旋来实现树的平衡;
**左旋:**把不符合规则的节点进行左旋转,使他取代父节点,父节点会变成他的左子节点;
**右旋:**把不符合规则的节点进行右旋转,使他取代父节点,父节点会变成他的右子节点;
扩展问题:红黑树与avl平衡二叉树
因为AVL平衡二叉树,需要保证左子树与右子树的高度差读最大为1,但红黑树不是这种完全的平衡,只需要部分达标就行,因此在进行删除或插入操作时,avl可能需要更多的旋转操作,而红黑树则是以非严格平衡来换取旋转次数的减,虽然在查询的时候avl的查询速度更快,但红黑树是对增删查操作性能折中的一个选择。