Java集合
Collection是集合List,Set,Queue最基本的接口;
Map是映射表最基本的接口;
了解如下基本数据结构
1.数组:增删慢,支持随机查找;
2.链表:增删快,不支持随机查找,只能顺序访问;
3.哈希表:查询速度快,在拉链法种查询速度取决于链表长度,无序;
4.队列:可以两端出入,可以用链表和数组实现;
有序无序是指插入顺序:先插在前,后插在后;
一.List
1.ArrayList
1.底层是数组;
2.是有序的,可重复;
3.线程不安全;
4.扩容1.5倍;
2.Vector
1.底层是数组;
2.有序,可重复;
3.线程安全,因为支持线程同步,访问速度就下来了;
4.扩容2倍;
3.LinkedList
1.底层是双向链表:可以当作队列,双向队列,堆,栈使用;
2.有序,可重复;
3.线程不安全;
二.Set
1.HashSet
1.底层是哈希表;key值通过hashCode()方法得到hash值,也就是数组下标,如果hash值相同,再通过equals方法比较两个对象是否相同,相同则覆盖,不同则放入链表中;
2.无序,不可重复;
3.存取速度快;
注:哈希相同就会产生哈希冲突,两种方法:哈希值下移,和拉链法(HashMap的时候会介绍);
2.TreeSet
1.底层是二叉树;
2.有序,不可重复;
3.按照指定的顺序排序,Integer和String可以按默认的TreeSet顺序排序,对于自定义的类必须实现Comparable接口,并重写compare()函数;
4.底层实现是TreeMap;
3.LinkedHashSet
1.无序;
2.继承HashSet,方法操作同HashSet,底层是LinkedHashMap实现;
三.Map
1.HashMap
1.无序,键值不可重复,值可重复;
2.底层哈希表;
3.线程不安全;
4.允许键值为null,扩容2倍;
5.实现结构:
- 数组+链表——Entry[] table;Entry是链表,存储有:key值,hash值,value和next;
原理很简单: key值通过hashCode()方法得到hash值,也就是数组下标,如果hash值相同,再通过equals方法比较两个对象是否相同,相同则覆盖,不同则放入链表中;放入链表的时候 是插在头部而不是尾部; - 四个参数
为甚么capacity必须保证2的n次方?
得到数组下标需要hash%capacity,我们比较两端代码
hash%capacity
hash%(capacity-1)可以发现,当capacity为2的n次方时,此操作和取模一摸一样;但在计算计算机中位运算的速度更快;所以采用下面的与运算; - java8实现——数组+链表+红黑树
查询速度取决于链表长度,当链表长度个数超过8个以后会转换成红黑树,时间复杂度可以从O(n)降到O(logn)
2.HashTable
1.底层哈希表;
2.键值不可重复,值可重复,无序;
3.线程安全;
4.键和值都不能为null;
5.HashTable是遗留类,继承自Dictionary类,虽然是线程安全,但并发性不如ConcurrentHashMap;
3.TreeMap
1.底层二叉树;
2.有序,键值不可重复,值可重复;
3.继承自SortedMap,默认对键进行升序排序,可以自定义Comparable接口,重定义排序规则;底层实现是红黑树(属于排序树的一种),加进去之后使用中序遍历就可以得到由小到大的顺序;
4.添加节点
a以根节点当前节点开始搜索。
b拿新节点的值和当前节点的值比较。
c如果新节点的值更大,则以当前节点的右子节点作为新的当前节点;如果新节点的值更小,则以当前节点的左子节点作为新的当前节点。
d重复 2、3 两个步骤,直到搜索到合适的叶子节点为止。
e将新节点添加为第 4 步找到的叶子节点的子节点;如果新节点更大,则添加为右子节点;否则添加为左子节点。
4.LinkedHashMap
1.HashMap的一个子类,保存了插入的顺序,使用Iterator进行迭代时,得到数据跟插入的顺序一样;
2.哈希表通过Iterator得到的数据就是无序的,比如HashSet,HashMap,因为他们插入的时候没有像数组一样记录顺序;
3.底层是HashMap和双向链表合二为一;
如何实现,双向链表的节点图如下,before,after和next的区别是前者可以连接不同hash之间的链表,只要将数据put到双向链表中,就可以实现按插入顺序访问该结构;
4.该集合有两种遍历方式,
访问顺序遍历: 每次访问完map中的元素后,会将该元素置入map的最后一位;
插入顺序遍历: 按插入时的顺序遍历
图片取自https://blog.csdn.net/a724888/article/details/80290276
图片取自https://blog.csdn.net/fengdongsuixin/article/details/95758243
5. 留个问题?LinkedHashMap如何实现LRU缓存?
5.ConcurrentHashMap
- ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。
- concurrencyLevel:segment数。默认是 16,
也就是说 ConcurrentHashMap 有 16 个 Segments,所以理论上,这个时候,最多可以同时支持 16 个线程并发写,只要它们的操作分别分布在不同的 Segment 上。这个值可以在初始化的时候设置为其他值,但是一旦初始化以后,它是不可以扩容的。 - Java8引入红黑树,同HashMap一样;