Map集合
Map接口特点(JDK8)
1.key-value(可以是任何引用类型的数据,会封装到hashMap$Node,Node实现了Entry接口,一对Key-value就是一个Entry)
2.Map中的key 不允许重复,hashset一样 (key相同的添加就相当于替换)
3.value可以重复
4.key中null只能为一个,value的null可以多个
5.key常用String类
6.kye-value存在一一对应关系 ,通过key能找到指定的value
7.线程不安全
HashMap k-v分析
@SuppressWarnings("all")
public static void main(String[] args) {
HashMap hashMap = new HashMap();
hashMap.put(1,"张三丰");
hashMap.put(2,"张无忌");
hashMap.put(3,"殷素素");
System.out.println(hashMap);
}
1.k-v最后是HsahMap$Node node = newNode(hash,key,value,null);
2.k-v为了方便遍历 会创建EntrySet集合,这个集合存放的元素类型是Entry,一个Entry对象就包含了k,v
EntrySet<Entry<k,v>>
transient Set<Map.Entry<K,V>> entrySet; entrySet中的entry<k,v>k-v指向newNode(hash,key,value,null)中的k-v。
3.Set<Map.Entry<K,V>>定义的类型是Map.Entry,但是存放的类型还是
HashMap $ Node类型(HashMap $ Node 实现了Map.Entry接口)
Set set = hashMap.entrySet(); //得到EntrySet
System.out.println(set.getClass()); //class java.util.HashMap$EntrySet
for (Object o : set) {
System.out.println(o.getClass()); //class java.util.HashMap$Node
}
Set set1 = hashMap.keySet();
System.out.println(set1.getClass()); //class java.util.HashMap$KeySet
Collection values = hashMap.values();
System.out.println(values.getClass()); //class java.util.HashMap$Values
4.Node放进Map.Entry 方便遍历,因为Map.Entry接口中有getKey 和getValue方法
所以我们可以向下转型Node–Map.Entry 拿到方法遍历
for (Object o : set) {
Map.Entry o1 = (Map.Entry) o;
System.out.println(o1.getKey());
System.out.println(o1.getValue());
}
HashMap K相同替换value源码
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p; //key值相同走到这
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value; //oldValue 替换之前的value
if (!onlyIfAbsent || oldValue == null) //onlyIfAbsent =false
e.value = value; //新的value
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
HashMap源码分析
HashMap hashMap = new HashMap();
hashMap.put("张无忌","diao");
hashMap.put("扫地僧","wudi");
hashMap.put("张三丰","nb");
hashMap.put("扫地僧","No1");
1.new HashMap(); DEFAULT_LOAD_FACTOR =0.75 transient Node<K,V>[] table; //HashMap$node
table = null
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
2. hashMap.put("张无忌","diao");
2.1 hash(key) 得到hash值 (h = key.hashCode()) ^ (h >>> 16);
2.2 final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict)
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0) //第一次table为null
n = (tab = resize()).length; //扩容16 核心代码 Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap]; 验证了table是 Node<K,V>[]
if ((p = tab[i = (n - 1) & hash]) == null) //得到一个索引位置是否有元素
tab[i] = newNode(hash, key, value, null); //当前位置没有元素 直接newNode
else { //当前位置有元素 3种情况
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k)))) //当前位置有元素,比较key是否相同,先比较key的hash 然后比较key的内容
e = p; //内容相同直接转下 进行value的替换 e = 当前node
else if (p instanceof TreeNode) //如果是树化的节点
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else { //当前位置是个链表(hash相同 内容不同)
for (int binCount = 0; ; ++binCount) { //死循环遍历
if ((e = p.next) == null) { //当前table位置的元素是链表的最后一个 添加到最后
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st 判断是否需要数化
treeifyBin(tab, hash);//如果超过8个 判断table容量是否超过64 没超过,则先扩容 否则数化
break;
}
if (e.hash == hash && //找到了有相同内容的元素
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key 此处是key相同 value的替换
V oldValue = e.value; //e = 当前table表的第一个元素 oldValue 记录旧的元素
if (!onlyIfAbsent || oldValue == null)
e.value = value; // 替换新的value
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
HashMap例题分析
Set treeSet = new HashSet();
A a1 = new A(1,"aa");
A a2 = new A(2,"bb");
treeSet.add(a1);
treeSet.add(a2);
a1.name ="cc";
treeSet.remove(a1); //tree
System.out.println(treeSet);//[A{id=1, name='cc'}, A{id=2, name='bb'}]
treeSet.add(new A(1,"cc")); //hash值不同 所以能添加进去
System.out.println(treeSet); //[A{id=1, name='cc'}, A{id=1, name='cc'}, A{id=2, name='bb'}]
treeSet.add(new A(1,"aa")); //hash相同 但是aa的内容已经变成cc了 所以可添加进去
System.out.println(treeSet); //[A{id=1, name='cc'}, A{id=1, name='cc'}, A{id=1, name='aa'}, A{id=2, name='bb'}]
HashTable
1.存放的键值对
2.k-v都不能为null 否则会异常 NullPointException
3.hashTable线程安全 HashMap线程不安全.
table 表 Hashtable$Entry 初始话大小为11 临界值 11*0.75 = 8
Hashtable 源码
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index]; //当前table位置的元素
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) { //当前位置存在元素,比较内容是否相同
V old = entry.value; //内容相同替换 不同则添加
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index); //第一次为null 添加
return null;
}
private void addEntry(int hash, K key, V value, int index) {
modCount++; //
Entry<?,?> tab[] = table;
if (count >= threshold) { //大于临界值8 扩容 11-->23 8-->17
// Rehash the table if the threshold is exceeded
rehash();
tab = table;
hash = key.hashCode();
index = (hash & 0x7FFFFFFF) % tab.length;
}
// Creates the new entry.
@SuppressWarnings("unchecked")
Entry<K,V> e = (Entry<K,V>) tab[index];
tab[index] = new Entry<>(hash, key, value, e); //添加到当前位置 e = Entry<K,V> next
count++;
}
protected void rehash() {
int oldCapacity = table.length;//第一次进来 = 11
Entry<?,?>[] oldMap = table;
// overflow-conscious code
int newCapacity = (oldCapacity << 1) + 1; //11*2+1
if (newCapacity - MAX_ARRAY_SIZE > 0) {
if (oldCapacity == MAX_ARRAY_SIZE)
// Keep running with MAX_ARRAY_SIZE buckets
return;
newCapacity = MAX_ARRAY_SIZE;
}
Entry<?,?>[] newMap = new Entry<?,?>[newCapacity];//23
modCount++;
threshold = (int)Math.min(newCapacity * loadFactor, MAX_ARRAY_SIZE + 1);//23*0.75
table = newMap;
for (int i = oldCapacity ; i-- > 0 ;) {
for (Entry<K,V> old = (Entry<K,V>)oldMap[i] ; old != null ; ) {
Entry<K,V> e = old;
old = old.next;
int index = (e.hash & 0x7FFFFFFF) % newCapacity;
e.next = (Entry<K,V>)newMap[index];
newMap[index] = e;
}
}
}
properties
1.properties继承自HashTable实现了Map接口,使用键值对保存数据
2.使用特点和Hashtable类似
3.properties可用于xxx.properties文件中,加载数据到properties类对象,并进行读取和修改
4.xxx.properties常作为配置文件
TreeMap
TreeMap treeMap = new TreeMap(); //不传comparator 默认会用 类的compareTo
treeMap.put(1,"jack");
treeMap.put(3,"tom");
treeMap.put(2,"mary");
public V put(K key, V value) {
Entry<K,V> t = root;
if (t == null) { //第一次进来
compare(key, key); // type (and possibly null) check
root = new Entry<>(key, value, null);
size = 1;
modCount++;
return null;
}
int cmp;
Entry<K,V> parent;
// split comparator and comparable paths
Comparator<? super K> cpr = comparator; //第二次进来,传了compatator
if (cpr != null) {
do {
parent = t;
cmp = cpr.compare(key, t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
else { // 第二次进来,没传compatator
if (key == null) //TreeMap 的key不允许为 null
throw new NullPointerException();
@SuppressWarnings("unchecked")
Comparable<? super K> k = (Comparable<? super K>) key;
do {
parent = t;
cmp = k.compareTo(t.key);
if (cmp < 0)
t = t.left;
else if (cmp > 0)
t = t.right;
else
return t.setValue(value);
} while (t != null);
}
Entry<K,V> e = new Entry<>(key, value, parent);
if (cmp < 0)
parent.left = e;
else
parent.right = e;
fixAfterInsertion(e);
size++;
modCount++;
return null;
}