一、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