HashMap 数据结构

有个疑问,hashmap是数组+链表的数据结构,那么它是怎么遍历的,为什么下面的代码遍历出来结果是有序的?是不是会有个keyset缓存着有序的key呢,答案是否定的
HashMap 遍历如下

        HashMap aa = new HashMap();
        aa.put("c", "aaa");
        aa.put("b", "bbb");
        aa.put("a", "ccc");
        Iterator it = aa.keySet().iterator();
        while(it.hasNext()) {
            String key = (String) it.next();
            System.out.println("key "+key+" value "+aa.get(key));
        }
        Iterator it2 = aa.entrySet().iterator();
        while(it.hasNext()) {
            Map.Entry entry = (Entry) it.next();
            System.out.println("key "+entry.getKey()+" value "+entry.getValue());
        }
结果
key a value ccc
key b value bbb
key c value aaa

从头看下,new HashMap() 的时候并没有创建数据结构

    public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }

添加数据的时候才会创建

    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[] tab; Node p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;   >>> 首次put数据会调用resize初始化槽数组
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null); >>> 如果槽上没有节点那么创建节点,挂在槽上
        else {
            Node e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p; >>>如果槽上第一个节点的key就是这个key的Entry
            else if (p instanceof TreeNode)
                e = ((TreeNode)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); >>> 新建Node
                        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))))  >>>如果链上找到了这个key对应的Entry
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;  >>>设置新的值
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
    public V get(Object key) {
        Node e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    final Node getNode(int hash, Object key) {
        Node[] tab; Node first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {   >>> first设置为映射到的槽位上
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))  >>>如果第一个key就equals,那么返回第一个
                return first;
            if ((e = first.next) != null) {   >>>如果链表不为空
                if (first instanceof TreeNode)
                    return ((TreeNode)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);   >>>遍历链表查找key比较equals
            }
        }
        return null;
    }

那么遍历时候it = aa.keySet().iterator()会调用到KeyIterator

    final class KeyIterator extends HashIterator
        implements Iterator {
        public final K next() { return nextNode().key; }
    }

它是继承自HashIterator

    abstract class HashIterator {
        HashIterator() {
            expectedModCount = modCount;
            Node[] t = table;
            current = next = null;
            index = 0;
            if (t != null && size > 0) { // advance to first entry
                do {} while (index < t.length && (next = t[index++]) == null);>>>遍历指向第一个不为空的槽
            }
        }
        final Node nextNode() {
            Node[] t;
            Node e = next;
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (e == null)
                throw new NoSuchElementException();
            if ((next = (current = e).next) == null && (t = table) != null) {  >>>next指向链表上的下一个
                do {} while (index < t.length && (next = t[index++]) == null); >>>如果链表遍历完了,那么指向下一个不为空的槽
            }
            return e;
        }
    }

现在问题解决了,KeyIterator只是个遍历工具,每次都是从一个非空槽位遍历整个链表,然后遍历下一个非空槽位的链表。
而Key是按hash算出来的,以上算出来hash值 a b c 就有顺序了,所以出现了keyset有序的假象,但其实是散列的。

另外说下HashSet,HashSet内部完全的HashMap,属桥接模式,那它怎么做到保存的数据不重复的,其实很简单,主要代码map.put(e, PRESENT) 而PRESENT是Object,HashMap里的key可不就是不重复的。So easy。

你可能感兴趣的:(HashMap 数据结构)