标签:remove new hashtable version move bool 简单 ret 介绍
map,键值对的集合,由于和pojo的结构和map类似,经常相互转换,或者作为带有特定标识的数据的集合存储方式二使用。
还是先放结论:
类型 | 数据结构 | 特点描述 |
---|---|---|
HashMap | 散列表(拉链法) | 最常用,无序,线程不安全 |
Hashtable | 散列表(拉链法) | 无序,线程安全 |
LinkedHashMap | 双向链表+散列表(拉链法) | 有序(插入顺),线程不安全 |
WeakHashMap | 散列表(拉链法) | 无序,线程不安全,弱引用键,保存对象被回收时,自动删除 |
TreeMap | 红黑树 | 有序(自动排序),线程不安全 |
先扔个概念摘自百度百科,哈希:
Hash,一般翻译做“散列”,也有直接音译为“哈希”的,就是把任意长度的输入(又叫做预映射, pre-image),通过散列算法,变换成固定长度的输出,该输出就是散列值。这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。
关于散列表:
散列表(Hash table,也叫哈希表),是根据关键码值(Key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数,存放记录的数组叫做散列表。
拉链法:
拉链法解决冲突的做法是:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。在拉链法中,装填因子α可以大于1,但一般均取α≤1
嗯,看了觉得很迷糊对吧,那么来一步步的说。
嗯,干巴巴的说果然还是很难理解,那么,现在有以下数据,要用散列表(拉链法)的方式进行存储,结果会是这么样的呢?
数据:
{"aa","1"},{"ab","2"},{"bA","3"},{"bB","4"},{"bC","5"}
顺便,在jdk1.8.0_121中,对String对象的哈希算法如下:
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
则计算以上数据key值得哈希值分别为: 3104,3105,3103,3104,3105
于是使用数组存储的话,大体是这么个印象
数组[3103] = {"bA","3"}
数组[3104] = {"aa","1"},{"bB","4"}
数组[3105] = {"ab","2"},{"bC","5"}
由于存在哈希值相同的情况,所以需要使用拉链法进行。由于要在数组同一位置存多个数据,采用单链的方式的话,需要记录链表下一个,next的值,所以需要包装一下。结果的话,大体是这种效果:
数组(3103) -> {"bA","3"}
数组(3104) -> {"aa","1"} -> {"bB","4"}
数组(3105) -> {"ab","2"} -> {"bC","5"}
经过以上说明,基本已经对hash有了概念的话,接下来就方便了。
还是上源码(jdk1.8.0_121,java.util.HashMap):
transient Node
static class Node
Node(int hash, K key, V value, Node<K,V> next) {......}
public final K getKey() {......}
public final V getValue() {......}
public final String toString() {......}
public final int hashCode() {......}
public final V setValue(V newValue) {......}
public final boolean equals(Object o) {......}
}
这是HashMap的关键,hashmap本质是散列表,是Node
示例:
HashMap<String,String> hashMap = new HashMap<String,String>();
hashMap.put("aa","1");
hashMap.put("ab","2");
hashMap.put("bA","3");
hashMap.put("bB","4");
hashMap.put("bC","5");
Iterator iter = hashMap.entrySet().iterator();
while (iter.hasNext()){
Map.Entry entry = (Map.Entry)iter.next();
System.out.println("key:"+entry.getKey()+",value:"+entry.getValue());
}
// 结果
// key:aa,value:1
// key:bB,value:4
// key:ab,value:2
// key:bC,value:5
// key:bA,value:3
由于HashMap和Hashtable非常相似,所以仅仅说一说Hashtable的区别。
其他的还有api不同,遍历种类不同,遍历顺序不同容量初值不同,hash算法不同等,姑且知道即可
首先,LinkedHashMap继承于HashMap,所以HashMap的相关功能都具备。然后,观察以下源码:
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
private static final long serialVersionUID = 3801124242820219131L;
transient LinkedHashMap.Entry<K,V> head;
transient LinkedHashMap.Entry<K,V> tail;
一个头,一个尾,扩展节点指前指后,对,双向链表。
LinkedHashMap比HashMap多维护的这个双向链表保存了map内容的插入顺序,并重写了相关的方法,使得对LinkedHashMap遍历时的,可以根据插入顺序,有序的遍历。
示例代码:
LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap<String,String>();
linkedHashMap.put("aa","1");
linkedHashMap.put("ab","2");
linkedHashMap.put("bA","3");
linkedHashMap.put("bB","4");
linkedHashMap.put("bC","5");
Iterator iter3 = linkedHashMap.entrySet().iterator();
while (iter3.hasNext()){
Map.Entry entry = (Map.Entry)iter3.next();
System.out.println("key:"+entry.getKey()+",value:"+entry.getValue());
}
// 结果
// key:aa,value:1
// key:ab,value:2
// key:bA,value:3
// key:bB,value:4
// key:bC,value:5
直降上例子:
Map<String, String> weak = new WeakHashMap<String, String>();
weak.put(new String("1"), "1");
weak.put(new String("2"), "2");
weak.put(new String("3"), "3");
weak.put(new String("4"), "4");
weak.put(new String("5"), "5");
weak.put(new String("6"), "6");
System.out.println(weak.size()); // 结果:6
System.gc(); //手动触发 Full GC
try {
Thread.sleep(50); //我的测试中发现必须sleep一下才能看到不一样的结果
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(weak.size()); // 结果:0
WeakHashMap的key对象,不被其他地方引用时,会在gc执行后自动被删除。这就是WeakHashMap最重要的特点:弱引用
总之,记住这一点即可,除非是用于做监控程序什么的,不然WeakHashMap很少被使用。
还是先上例子:
TreeMap<String,String> treeMap = new TreeMap<String,String>();
treeMap.put("bB","4");
treeMap.put("bC","5");
treeMap.put("aa","1");
treeMap.put("ab","2");
treeMap.put("bA","3");
Iterator iter4 = treeMap.entrySet().iterator();
while (iter4.hasNext()){
Map.Entry entry = (Map.Entry)iter4.next();
System.out.println("key:"+entry.getKey()+",value:"+entry.getValue());
}
// 结果
// key:aa,value:1
// key:ab,value:2
// key:bA,value:3
// key:bB,value:4
// key:bC,value:5
可以发现,遍历的输出结果,自动的进行了排序(默认按照键,可在构造中设置Comparator)。
另外,TreeMap的本质是红黑树,适用于有序的整理遍历。关键代码如下:
private transient Entry<K,V> root;
static final class Entry<K,V> implements Map.Entry<K,V> {
K key; // 键
V value; // 值
Entry<K,V> left; // 左节点
Entry<K,V> right; // 右节点
Entry<K,V> parent; // 父节点
boolean color = BLACK;// 自身颜色
Entry(K key, V value, Entry<K,V> parent) {......}
public K getKey() {......}
public V getValue() {......}
public V setValue(V value) {......}
public boolean equals(Object o) {......}
public int hashCode() {......}
public String toString() {......}
}
红黑树比较麻烦,之后再介绍一些数据在说一说,这里对TreeMap,记住本质是红黑树,自动排序即可。
map大体就是以上说明,趁着这个机会,也把set稍微总结下吧。
类型 | 实现使用map对象 | 数据结构 |
---|---|---|
HashSet | HashMap | 散列表(拉链法) |
LinkedHashSet | LinkedHashMap | 双向链表+散列表(拉链法) |
TreeMap | TreeMap | 红黑树 |
标签:remove new hashtable version move bool 简单 ret 介绍
原文地址:https://www.cnblogs.com/changfanchangle/p/map.html