java集合底层分析

一、set
个人开发,set用的比list,map用的少,常用的是hashset,这里重点说hashset

1.1
Hashset
1.1.1
底层是hashmap,利用hashmap的key不能重复的方法来完成hashset的功能,而定义hashset时hashmap的value 用一个static final PRESENT 来填充(无实际用途,假值)
1.1.2
hashset继承了AbstractSet 而这个abstractset里面重写了equals,hashcode,removeAll(Collection C) 方法。这里的重写,会导致如下代码:
HashSet<String> set1 = new HashSet<String>();
TreeSet<String> set2 = new TreeSet<String>();
System.out.println(set1.equals(set2));
System.out.println(set1.hashCode() == set2.hashCode());
System.out.println(set1.hashCode());
结果是 true true 0
1.1.3
这里多说一句:最顶层的Collection里面定义里大部分方法,适配器AbstractCollerction里面全部继承下来(list和set公用的部分),在AbstractSet实现了HashSet 和TreeSet 公用的部分
1.2
Treeset
用的非常少,底层也是TreeMap,不过是NavigableMap 实现了NavigableMap和SortSet接口
1.3
LinkedHashSet
继承HashSet 底层是靠有序的LinkedMap实现
公共:都实现set接口,但是都不是现场安全的

二、list

和下面的map一起,都有一个变量,叫做modecount(修改计数器)
使用了 Fail-Fast 机制 
当遍历的是否修改了遍历的集合内部元素,会导致iterator和数量和modecount不同,抛出异常
因此,对于线程不安全的集合遍历,推荐使用iterator

2.1
ArrayList
2.1.1
显示AbastractList接口,底层是数组
2.2
LinkedList
2.2.1
底层链表
2.2.2
在实现AbstractList接口之前,实现了AbstractSequentialList ,为了更快速的迭代而是用listIterator自己的链表迭代器

2.3
Vector
2.3.1
底层还是数组
2.3.2
Vector 线程安全(不绝对)
为什么安全,因为大部分方法(修改Vector内容的)都加了重量级锁synchronized

Vector vector = new Vector();
public void put(String element) {
if (!vector.contains(element)){//线程不安全
vector.add(element);//线程安全
}
//do something 导致线程不安全
}
如何线程安全呢
a
synchronized (this) {
if (!vector.contains(element)) {
vector.add(element);
}
}
b(模拟单例模式)
if (!vector.contains(element)){
synchronized (this) {
if (!vector.contains(element)) {
vector.add(element);
}
}
}

三、map
map的遍历,4种方式 1 keyset 2 iterator 3 map.values 直接遍历value 4 entyrSet
大数据量推荐4, 多并发推荐2

3.1
hashmap
3.1.1
hash结构

Enter[ ] 内部实体
3.1.2
put 是比较Entry的e的hashcode,当hashcode相同的时候,才会使用equals,所以hashmap的效率高。所以在重写对象的equals方法的同时,也要重写hashcode 方法。
3.1.2.1
hashmap的hash碰撞的处理
hash碰撞指:对象拥有相同的hashcode,但是对象不同,其概率为数学问题
通常是两种方法:链表法和开放地址法。链表法就是将相同hash值的对象组织成一个链表放在hash值对应的槽位;开放地址法是通过一个探测算法,当某个槽位已经被占据的情况下继续查找下一个可以使用的槽位。(引用)
hashmap使用前者,所以会出现如上图所示数据结构
3.1.3
在jdk1.8之前,hashmap是数组和链表的结合体,1.8之后,变成了数组和树和联合体(待研究)。时间复杂度由O(n)变成了O(logn)
3.1.4
hashmap扩容
hashmap当存储内容过多,导致不能支持快速查找
其中参数:

没有对外提供扩容接口,当数据符合设置的条件后,自动扩容处理,长度*2。
扩容效率较低,需要2次遍历,第一次遍历数组table,第二次遍历 链表Entry
如果可以预计的map长度很大,可是初始化设定map长度,避免扩容
3.1.5
遍历
keyset和EntyrSet 是2种底层实现, entrySet()遍历的效率会比keySet()高
具体不聊,会用即可
3.1.6
由于hashmap是线程不安全的,所以在迭代的时候,会将modCount赋值到迭代器的expectedModCount属性中,然后进行迭代,
如果在迭代的过程中HashMap被其他线程修改了,modCount的数值就会发生变化,
这个时候expectedModCount和ModCount不相等,
迭代器就会抛出ConcurrentModificationException()异常
3.2
linkedmap
3.2.1
底层双向链表,可以排序,其他逻辑同hashmap,没有太深入研究
3.3
treemap
3.3.1
底层红黑树,所以get,put,remove等查询相关的操作时间复杂度为O(log(n))
3.3.2
tree的key的类,必须实现comparable接口,因为treemap会进行内部排序,通过comparable接口的方法在确定顺序,没有重写运行时会报错。
功能
hashmap
treemap
linkedhashmap
order
通过内部key的类compabale确定
插入顺序
get/put/remove O()
O(1)
O(log(n))红黑树遍历
O(1)
interface
map
NavigableMap,SortedMap,map
map
null key
allow 只允许一个null key
 not allow
allow 只允许一个null key
线程安全
不安全
底层实现
hash链表
红黑树
双向hash表
modcount
都有modcount,在迭代时判断,抛出异常
3.4
WeakHashMap
没用过,没听说过,只是总结的是否顺便带上它了
3.4.1
弱引用,对象4种引用之一,weakhashmap里的Entry 是弱引用,在垃圾回收前后可能导致size()不同。用于缓存中使用。由于没有实际应用场景,不清楚这类map好处
3.5
hashtable
3.5.1
比hashmap出来的早 ,hashmap基本可以替代hashtable
底层实现不同,感觉不重要,不列举了
3.5.2
key value 均不能为空,比hashmap多了一层判断
3.5.3 (hashtable核心点)
hashmap和hashtable区别
(如果说hashmap可以全面替代hashtable,为什么)

可以看到HashTable默认的初始大小为11,之后每次扩充为原来的2n+1。HashMap默认的初始化大小为16,之后每次扩充为原来的2倍。还有我没列出代码的一点,就是如果在创建时给定了初始化大小,那么HashTable会直接使用你给定的大小,而HashMap会将其扩充为2的幂次方大小。
也就是说HashTable会尽量使用素数、奇数。而HashMap则总是使用2的幂作为哈希表的大小。我们知道当哈希表的大小为素数时,简单的取模哈希的结果会更加均匀(具体证明,见这篇 文章 ),所以单从这一点上看,HashTable的哈希表大小选择,似乎更高明些。但另一方面我们又知道,在取模计算时,如果模数是2的幂,那么我们可以直接使用位运算来得到结果,效率要大大高于做除法。所以从hash计算的效率上,又是HashMap更胜一筹。
所以,事实就是HashMap为了加快hash的速度,将哈希表的大小固定为了2的幂。当然这引入了哈希分布不均匀的问题,所以HashMap为解决这问题,又对hash算法做了一些改动。具体我们来看看,在获取了key对象的hashCode之后,HashTable和HashMap分别是怎样将他们hash到确定的哈希桶(Entry数组位置)中的。
hashtable 初始容量是11,每次增加2N+1
hashmap 初始容量是16,每次增加2N
所以,hashmap容量都是2的幂,取模时用位运算即可,不用计算除法
哈希表的大小为素数时,简单的取模哈希的结果会更加均匀。

HashTable ht = new HashTable(1023);
HastMap hm = new HashMap(1024);
这种定义方式,hashtable容量是1023,而hashmap容量是1024
总结一下吧
hashtable已经被淘汰,不用就好(非并发方向)
在并发方向hashtable还有自己的部分优势

3.6
ConcurrentHashMap
//TODO :略难,有空再研究

四 工具类 //TODO

4.1 Arrays

4.2 Collections

猜你喜欢

转载自blog.csdn.net/pengjj1223/article/details/80664363