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(' '); } }
- HashMap(2): EntrySet、KeySet实现原理
- HashMap底层之entrySet,keySet,value实现
- HashMap的entrySet与keySet
- HashMap的entrySet与keySet
- HashMap中的keySet()和entrySet()
- HashMap之entrySet( )底层实现原理分析
- keySet 与entrySet 遍历HashMap性能差别
- keySet 与entrySet 遍历HashMap性能差别
- keySet 与entrySet 遍历HashMap性能差别
- HashMap遍历:entrySet和keySet的比较
- jdk:HashMap的keySet(),values(),entrySet()
- keySet 与entrySet 遍历HashMap性能差别
- HashMap遍历--entrySet()与keySet()比较
- keySet 与entrySet 遍历HashMap性能差别
- keySet 与entrySet 遍历HashMap性能差别
- entrySet keySet
- 实在没想到系列——HashMap实现底层细节之keySet,values,entrySet的一个底层实现细节
- HashMap----- 遍历---- keySet -----entrySet
- 基于node权威指南书籍中的blog练习,已改成express4.0+mongoose+jade
- 模仿Wireshark网络抓包工具实现---c++
- 【剑指offer】实现一个函数来替换字符串中的空格
- 05 关于AVR ATmega8 串口数据协议传输的奇偶校验码的一点想法
- react-native setNativeProps 详解
- HashMap(2): EntrySet、KeySet实现原理
- 关于LM576驱动LED发光二极管(LM567鉴频红外测试)记录
- JS和jsp区别
- 使用Ionic + Apache Cordova开发跨平台混合型的移动应用
- 《机器学习实战》第二章:k-近邻算法(3)手写数字识别
- These Were The Best Machine Learning Breakthroughs Of 2016
- android读取本地图片按照指定尺寸缩放
- TCP之控制connect超时
- Caffe+Python接口测试MNIST--下载和准备数据