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);

java 双向队列 peek java 双向map_开发语言


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。

java 双向队列 peek java 双向map_ci_02


java 双向队列 peek java 双向map_java 双向队列 peek_03

3.Set<Map.Entry<K,V>>定义的类型是Map.Entry,但是存放的类型还是

HashMap $ Node类型(HashMap $ Node 实现了Map.Entry接口)

java 双向队列 peek java 双向map_开发语言_04

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方法

java 双向队列 peek java 双向map_java_05


所以我们可以向下转型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线程不安全.

java 双向队列 peek java 双向map_面试_06


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;
    }