HashMap(2): EntrySet、KeySet实现原理

来源:互联网 发布:苹果随意定位软件 编辑:程序博客网 时间:2024/05/16 19:41

一.概述

HashMap里面保存的数据最底层是一个Entry型的数组,这个Entry则保留了一个键值对,还有一个指向下一个Entry的指针。所以HashMap是一种结合了数组和链表的结构。因此,我们有3种对数据的观测方式:keySet,values,entrySet。

keySet是从key的值角度出发的结果。它里面包含了这个键值对表里面的所有键的值的集合,因为HashMap明确规定一个键只能对应一个值,所以不会有重复的key存在,这也就是为什么可以用集合来装key。

values则是从键值对的值的角度看这个映射表,因为可以有多个key对应一个值,所以可能有多个相同的values。

entrySet是从键值对的角度思考这个问题,它返回一个键值对的集合。(键值对相等当且仅当键和值都相等)。

二.原理

1.keySet.iterator()

HashMap中的keySet源码:

    public Set<K> keySet() {        Set<K> ks = keySet;        return (ks != null ? ks : (keySet = new KeySet()));    }   private final class KeySet extends AbstractSet<K> {      public Iterator<K> iterator() {          return newKeyIterator();      }      public int size() {          return size;      }      public boolean contains(Object o) {          return containsKey(o);      }      public boolean remove(Object o) {          return HashMap.this.removeEntryForKey(o) != null;      }      public void clear() {          HashMap.this.clear();      }  }

由以上源码可知,当调用hashmap.keySet()方法时,若keySet为空,则返回一个新建的内部类KeySet(); 但是我们发现它有个iterator()方法,我们在敲代码中经常使用这个迭代器迭代取出HashMap对象中的key和value,再深入看:

Iterator newKeyIterator() {    return new KeyIterator();}

HashMap内部类KeyIterator :

private final class KeyIterator extends HashIterator {    public Object next() {        return nextEntry().getKey();    }       final HashMap this$0;      private KeyIterator() {        this$0 = HashMap.this;        super();    }}

HashIterator :

private abstract class HashIterator implements Iterator {    public final boolean hasNext() {        return next != null;    }    final HashMap.Entry nextEntry() {        if (modCount != expectedModCount)            throw new ConcurrentModificationException();        HashMap.Entry entry = next;        if (entry == null)            throw new NoSuchElementException();        if ((next = entry.next) == null) {            for (HashMap.Entry aentry[] = table; index < aentry.length                    && (next = aentry[index++]) == null;)                ;        }        current = entry;        return entry;    }    public void remove() {        if (current == null)            throw new IllegalStateException();        if (modCount != expectedModCount) {            throw new ConcurrentModificationException();        } else {            Object obj = current.key;            current = null;            removeEntryForKey(obj);            expectedModCount = modCount;            return;        }    }    HashMap.Entry next;    int expectedModCount;    int index;    HashMap.Entry current;    final HashMap this$0;    HashIterator() {        this$0 = HashMap.this;        super();        expectedModCount = modCount;        if (size > 0) {            for (HashMap.Entry aentry[] = table; index < aentry.length                    && (next = aentry[index++]) == null;)                ;        }    }}

由此可以Iterator是继承了这个HashIterator 这个内部类,而HashIterator 又访问了外部HashmMap的一些属性得以访问到整个对象的table数组的所有Entry的。

2.输出hashmap.keySet();

写个测试的案例:

@Testpublic void testPrint(){    Map<String, Object> map = new HashMap<String, Object>();    map.put("now", "u");    map.put("see", "me");    System.out.println(map.keySet());}

输出结果为:

[see, now]

当我们调用map.keySet时候,为什么会打印出keySet的这个结果呢?原理是什么?仿照HashMap写一个自定义的Iterator

package MyMap.Demo1;import java.util.AbstractSet;import java.util.ArrayList;import java.util.Iterator;import java.util.Set;public class MyTestIterator {    public static void main(String[] args) {    TestIterator t = new TestIterator();        Set<Integer> set = t.keySet();        System.out.println(set);    }   }class TestIterator {    public Set<Integer> keySet() {        final ArrayList<Integer> result = new ArrayList<Integer>();        result.add(1);        result.add(2);        result.add(3);        Set<Integer> keySet = new AbstractSet<Integer>() {            public Iterator<Integer> iterator() {                return new Iterator<Integer>() {                    private Iterator<Integer> i = result.iterator();                    @Override                    public boolean hasNext() {                        System.out.println("===========hasNext");                        return i.hasNext();                    }                    @Override                    public Integer next() {                        System.out.println("===========next");                        return i.next();                    }                    @Override                    public void remove() {                        System.out.println("===========remove");                        i.remove();                    }                };            }            @Override            public int size() {                return 0;            }        };        return keySet;    }}

打印结果为:

===========hasNext===========hasNext===========next===========hasNext===========next===========hasNext===========next===========hasNext[1, 2, 3]

是在哪里调用了keySet的iterator方法呢?在所有的输出语句打个断点,原来当我们使用
System.out.println( t.keySet()); 输出Set<Integer>对象时,由于AbstractSet类继承了AbstractCollection类,而AbstractCollection又重写了Object类的toString()方法,因此在输出时调用了AbstractCollection.java类中的toString方法,重写的toString()方法又调用了iterator,从而迭代取出结果拼接成字符串。

public String toString() {       Iterator<E> it = iterator();       if (! it.hasNext())           return "[]";       StringBuilder sb = new StringBuilder();       sb.append('[');       for (;;) {           E e = it.next();           sb.append(e == this ? "(this Collection)" : e);           if (! it.hasNext())               return sb.append(']').toString();           sb.append(',').append(' ');       }   }
0 0
原创粉丝点击