jdk:HashMap的keySet(),values(),entrySet()

在api文档中这三个方法返回的都是视图,今天从源码层面上分析,为什么是视图。

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

values():

public Collection<V> values() {
        Collection<V> vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    private final class Values extends AbstractCollection<V> {
        public Iterator<V> iterator() {
            return newValueIterator();
        }
        public int size() {
            return size;
        }
        public boolean contains(Object o) {
            return containsValue(o);
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

entrySet():

 public Set<Map.Entry<K,V>> entrySet() {
    return entrySet0();
    }

    private Set<Map.Entry<K,V>> entrySet0() {
        Set<Map.Entry<K,V>> es = entrySet;
        return es != null ? es : (entrySet = new EntrySet());
    }

    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
        public Iterator<Map.Entry<K,V>> iterator() {
            return newEntryIterator();
        }
        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry<K,V> e = (Map.Entry<K,V>) o;
            Entry<K,V> candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }
        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }
        public int size() {
            return size;
        }
        public void clear() {
            HashMap.this.clear();
        }
    }

从iterator()以外的其它方法已经可以看出一些端倪,它们内部都是调用的HashMap的方法进行相关的操作。
现在主要关注iterator()方法。该方法会在遍历集合时调用,如果明白了它,就可以知道遍历时,具体在遍历什么。

下面是三个iterator()方法的具体实现:

 Iterator<K> newKeyIterator()   {
     return new KeyIterator();
 }
 Iterator<V> newValueIterator()   {
     return new ValueIterator();
 }
 Iterator<Map.Entry<K,V>> newEntryIterator()   {
     return new EntryIterator();
 }

keyIterator、ValueIterator、EntryIterator定义如下:

private final class ValueIterator extends HashIterator<V> {
  public V next() {
       return nextEntry().value;
  }
}

private final class KeyIterator extends HashIterator<K> {
    public K next() {
        return nextEntry().getKey();
    }
}

private final class EntryIterator extends HashIterator<Map.Entry<K,V>> {
    public Map.Entry<K,V> next() {
        return nextEntry();
    }
}

到这里我们可以看到他们都继承自同一个HashIterator<E>,下面是HashIterator<E>源码:

private abstract class HashIterator<E> implements Iterator<E> {
    Entry<K,V> next;    // next entry to return
    int expectedModCount;   // For fast-fail
    int index;      // current slot
    Entry<K,V> current; // current entry

    HashIterator() {
        expectedModCount = modCount;
        if (size > 0) { // advance to first entry
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Entry<K,V> nextEntry() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Entry<K,V> e = next;
        if (e == null)
            throw new NoSuchElementException();

        if ((next = e.next) == null) {
            Entry[] t = table;
            while (index < t.length && (next = t[index++]) == null)
                ;
        }
 current = e;
        return e;
    }

    public void remove() {
        if (current == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        Object k = current.key;
        current = null;
        HashMap.this.removeEntryForKey(k);
        expectedModCount = modCount;
    }

}

HashIterator<E>实现了Iterator<E>接口但并没有实现next方法,所以HashIterator<E>被声明为抽象类,next方法在keyIterator、ValueIterator、EntryIterator中都进行了具体的实现,其实现都基于HashIterator<E>的nextEntry()方法。下面来详细分析下nextEntry()方法。

nextEntry()顾名思义,应该是返回下一个Entry对象,前面说到HashMap的键值对都存储在数组+链表结构的Entry对象中,具体可以参考另一篇博客从源码解读HashMap数据结构,根绝keySet(),values(),entrySet()返回视图的结论来看,这里的nextEntry()访问的仍然应该是HashMap的数据,也就是说三个返回的集合都是没有实体的,仍然是换了一种形式访问HashMap的数据而已,所进行的操作也直接反映在HashMap上,下面来具体看nextEntry()方法以验证这种推测:
首先看HashIterator的构造方法:

 HashIterator() {
    expectedModCount = modCount;
    if (size > 0) { // advance to first entry
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
            ;
    }
}

首先会拿到HashMap的结构基础Entry[] table,while循环就是找到第一个有数据的链表,在没有数据的情况下t[index++]==null

再看 nextEntry():

final Entry<K,V> nextEntry() {
    if (modCount != expectedModCount)
        throw new ConcurrentModificationException();
    Entry<K,V> e = next;
    if (e == null)
        throw new NoSuchElementException();

    if ((next = e.next) == null) {
        Entry[] t = table;
        while (index < t.length && (next = t[index++]) == null)
            ;
    }
    current = e;
    return e;
}

核心意思是如果当一个链表通过e.next遍历完以后,会再次通过while循环,寻找下一个有数据的链表。

看得出来,nextEntry拿到的确实仍是HashMap的数据,然后在keyIterator、ValueIterator、EntryIterator的next方法中分别拿nextEntry().getKey()、nextEntry().value、nextEntry()。

所以说keySet(),values(),entrySet()返回的集合是没有额外的数据实体的,都依赖于HashMap的数组+链表结构的Entry数据。所以称返回的是视图。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值