HashMap里面保存的数据最底层是一个Entry型的数组,这个Entry则保留了一个键值对,还有一个指向下一个Entry的指针。所以HashMap是一种结合了数组和链表的结构。因此,我们有3种对数据的观测方式:keySet,values,entrySet。
keySet是从key的值角度出发的结果。它里面包含了这个键值对表里面的所有键的值的集合,因为HashMap明确规定一个键只能对应一个值,所以不会有重复的key存在,这也就是为什么可以用集合来装key。
values则是从键值对的值的角度看这个映射表,因为可以有多个key对应一个值,所以可能有多个相同的values。
entrySet是从键值对的角度思考这个问题,它返回一个键值对的集合。(键值对相等当且仅当键和值都相等)。
HashMap中的keySet源码:
public Set keySet() {
Set ks = keySet;
return (ks != null ? ks : (keySet = new KeySet()));
}
private final class KeySet extends AbstractSet {
public Iterator 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的。
写个测试的案例:
@Test
public 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 set = t.keySet();
System.out.println(set);
}
}
class TestIterator {
public Set keySet() {
final ArrayList result = new ArrayList();
result.add(1);
result.add(2);
result.add(3);
Set keySet = new AbstractSet() {
public Iterator iterator() {
return new Iterator() {
private Iterator 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
对象时,由于AbstractSet类继承了AbstractCollection类,而AbstractCollection又重写了Object类的toString()方法,因此在输出时调用了AbstractCollection.java类中的toString方法,重写的toString()方法又调用了iterator,从而迭代取出结果拼接成字符串。
public String toString() {
Iterator 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(' ');
}
}