从源码角度分析如何遍历HashMap
本文是从源码部分探究如何遍历HashMap中所有的键值对,如果需要实际应用,直接看 具体实现部分即可。
如何遍历
这里我就不卖关子了,如果需要遍历的话大概可以从以下的方面入手:
迭代器遍历,Map中增强型for循环遍历,HashMap中的forEach方法循环遍历,以流的形式遍历。
了解迭代器
在进入这个话题之前我们先来了解两个迭代器Iterator与SplIterator。
Iterator
简单来说迭代器是用来访问集合一个接口,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而实现解耦。
迭代器类上的注释是这么描述该接口的
集合上的迭代器。迭代器在Java集合框架中取代了枚举。迭代器与枚举的区别
有两点:
迭代器允许调用者在迭代期间以良好定义的语义从底层集合中删除元素。
方法名称得到了改进。
SplIterator
Spliterator是java1.8新增的接口,即为splitable iterator(可分裂迭代器)的意思。该接口是java为了并行遍历数据源中的元素而设计的。
我们都知道HashMap的底层实现是数组 + 链表或红黑树。那么其分裂就是以链表或红黑树为单位来分裂的(也有的人叫桶-bucket)。
大概是这样:假如我现在要遍历所有的数据,如下,SplIterator对应集合中有三颗红黑树。
按以上方法,只能有一个迭代器去遍历数据,如果要更快一点呢?比如我想去并发遍历所有数据。先给它分裂一下,把上面的集合变成,以下这样,这样的话我有两个Spliterator就可以并行遍历所有的元素了。
HashMap中的迭代器
我们打开HashMap的源码,可以看到以下的迭代器。我们慢慢分析
这里的话,我们就分析上面的的普通迭代器了,可分裂迭代器,你知道它是可分裂的并行迭代器的就可以了。
HashIterator
一个抽象类,其他的普通迭代器都继承该迭代器,用于对HashMap的单个节点Node<K,V>进行迭代。
/* ------------------------------------------------------------ */
// iterators
abstract class HashIterator {
Node<K,V> next; // next entry to return
Node<K,V> current; // current entry
int expectedModCount; // for fast-fail
int index; // current slot
HashIterator() {
expectedModCount = modCount;
Node<K,V>[] 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);
}
}
public final boolean hasNext() {
return next != null;
}
final Node<K,V> nextNode() {
Node<K,V>[] t;
Node<K,V> e = next;
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
if (e == null)
throw new NoSuchElementException();
if ((next = (current = e).next) == null && (t = table) != null) {
do {} while (index < t.length && (next = t[index++]) == null);
}
return e;
}
public final void remove() {
Node<K,V> p = current;
if (p == null)
throw new IllegalStateException();
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
current = null;
K key = p.key;
removeNode(hash(key), key, null, false, false);
expectedModCount = modCount;
}
}
KeyIterator
键值对,键值对,肯定包含键,这个就是键的迭代器。该迭代器专门用于键的迭代,继承了HashIterator,实现了Iterator接口。
final class KeyIterator extends HashIterator
implements Iterator<K> {
public final K next() { return nextNode().key; }
}
ValueIterator
值的迭代器。跟KeyIterator一样,继承了HashIterator,实现了Iterator接口。
final class ValueIterator extends HashIterator
implements Iterator<V> {
public final V next() { return nextNode().value; }
}
EntryIterator
Entry的迭代器。该对象可以简单理解为,包含了键值的对象。
final class EntryIterator extends HashIterator
implements Iterator<Map.Entry<K,V>> {
public final Map.Entry<K,V> next() { return nextNode(); }
}
几个获取HashMap里数据的方法
打开源码找到这几个方法
keySet()以及KeySet
该方法用于获取所有的key集合,K表示泛型。注意KeySet类中的方法。
/*
返回一个该映射包含key的Set集合
*/
public Set<K> keySet() {
Set<K> ks;
return (ks = keySet) == null ? (keySet = new KeySet()) : ks;
}
/*
KeySet的数据结构 注意以下的方法
*/
final class KeySet extends AbstractSet<K> {
//获取size
public final int size() { return size; }
//置空
public final void clear() { HashMap.this.clear(); }
//获取迭代器
public final Iterator<K> iterator() { return new KeyIterator(); }
//是否包含
public final boolean contains(Object o) { return containsKey(o); }
//移除元素
public final boolean remove(Object key) {
return removeNode(hash(key), key, null, false, true) != null;
}
//返回一个可分裂迭代器
public final Spliterator<K> spliterator() {
return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
//增强型for循环 注意这里的方法也可以用来遍历KeySet集合
public final void forEach(Consumer<? super K> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.key);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
values()以及Values
跟上面的差不多keySet差不多。
/**
* 返回一个该映射包含值的Collection集合
*/
public Collection<V> values() {
Collection<V> vs;
return (vs = values) == null ? (values = new Values()) : vs;
}
//HashMap中所有值的集合
final class Values extends AbstractCollection<V> {
//大小
public final int size() { return size; }
//置空
public final void clear() { HashMap.this.clear(); }
//迭代器
public final Iterator<V> iterator() { return new ValueIterator(); }
//是否包含
public final boolean contains(Object o) { return containsValue(o); }
//可分裂迭代器
public final Spliterator<V> spliterator() {
return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);
}
//forEach方法
public final void forEach(Consumer<? super V> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e.value);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
entrySet()以及EntrySet
返回所有键值对的实体。
/*
返回一个键值对实体的Set集合
*/
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
//键值对实体
final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
//大小
public final int size() { return size; }
//置空
public final void clear() { HashMap.this.clear(); }
//迭代器
public final Iterator<Map.Entry<K,V>> iterator() {
return new EntryIterator();
}
//是否包含
public final boolean contains(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Node<K,V> candidate = getNode(hash(key), key);
return candidate != null && candidate.equals(e);
}
//移除元素
public final boolean remove(Object o) {
if (o instanceof Map.Entry) {
Map.Entry<?,?> e = (Map.Entry<?,?>) o;
Object key = e.getKey();
Object value = e.getValue();
return removeNode(hash(key), key, value, true, true) != null;
}
return false;
}
//可分裂迭代器
public final Spliterator<Map.Entry<K,V>> spliterator() {
return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);
}
//forEach方法
public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
Node<K,V>[] tab;
if (action == null)
throw new NullPointerException();
if (size > 0 && (tab = table) != null) {
int mc = modCount;
for (int i = 0; i < tab.length; ++i) {
for (Node<K,V> e = tab[i]; e != null; e = e.next)
action.accept(e);
}
if (modCount != mc)
throw new ConcurrentModificationException();
}
}
}
流的相关信息
在讲这个之前,我们看HashMap中的entrySet() 这个方法。该方法返回的是一个Set集合。该集合里面的元素是Entry,也就是返回的是存储键值对的一个Set集合。
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
而Set接口继承了Collection接口,Collection接口中有这样的两个方法。
/*
*返回以此集合为源的顺序流。
当split operator()方法无法返回不可变、并发或后期绑定的split
operator时,应重写此方法。(有关详细信息,请参见spliterator()。
*/
default Stream<E> stream() {
return StreamSupport.stream(spliterator(), false);
}
/*
返回一个可能的并行流,以此集合作为其源。允许此方法返回顺序流。
当split operator()方法无法返回不可变、并发或后期绑定的
split operator时,
应重写此方法。(有关详细信息,请参见spliterator()。
*/
default Stream<E> parallelStream() {
return StreamSupport.stream(spliterator(), true);
}
通过注释,我们很容易明白,两个方法的作用。也就是以流的方式操作集合,而下面的parallelStream()是为了并行操作该流创建的。感兴趣的话,大家可以ctrl + 左键,点进去源码看看。
具体遍历
好了,我们准备工作已经做的差不多了,现在就开始遍历HashMap吧。
先创建出需要遍历的HashMap。
HashMap<Integer , String> hashMap = new HashMap<>(8);
hashMap.put(1001 , "张三");
hashMap.put(1002 , "李四");
hashMap.put(1003 , "王五");
hashMap.put(1004 , "赵六");
hashMap.put(1005 , "周七");
hashMap.put(1006 , "郑八");
迭代器遍历
在迭代器中,我们看到有很多迭代器,都可用于遍历。但是由于无法用值定位到键,所以下面只展示了key与Entry的遍历。
key迭代遍历所有数据
不推荐使用,因为取值的时候调用了HashMap的get方法。
//迭代器实现遍历 - 其中三个普通迭代器都可以使用此方法遍历,这里以键的形
Iterator<Integer> iterator = hashMap.keySet().iterator();
while (iterator.hasNext()) {
Integer key = iterator.next();
//注意这一行 调用了get方法 可以想想有没有必要
System.out.println("key : " + key + " , value: " + hashMap.get(key));
}
Entry迭代遍历所有数据
推荐使用
Iterator<Map.Entry<Integer , String>> iterator1 = hashMap.entrySet().iterator();
while (iterator1.hasNext()) {
Map.Entry<Integer , String> entry = iterator1.next();
System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
}
Map中增强型for循环遍历
key遍历
还是需要调用get方法,不推荐。
for (Integer integer: hashMap.keySet()) {
System.out.println("key : " + integer + " , value: " + hashMap.get(integer));
}
Entry遍历
for (Map.Entry<Integer , String> entry: hashMap.entrySet()) {
System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
}
HashMap中的forEach方法循环遍历
HashMap重写Map的forEach方法遍历
hashMap.forEach((k,v)-> {
System.out.println("key : " + k + " , value: " + v);
}
);
Key的forEach方法遍历
还是调用了get方法
hashMap.keySet().forEach(k -> {
System.out.println("key : " + k + " , value: " + hashMap.get(k));
}
);
Entry的forEach方法遍历
hashMap.entrySet().forEach(entry -> {
System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
}
);
以流的形式遍历
普通流遍历key
hashMap.keySet().stream().forEach(integer -> {
System.out.println("key : " + integer + " , value: " + hashMap.get(integer));
});
普通流遍历Entry
hashMap.entrySet().stream().forEach( entry-> {
System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
});
并行流遍历key
hashMap.entrySet().parallelStream().forEach( key-> {
System.out.println("key : " + key + " , value: " + hashMap.get(key));
});
并行流遍历Entry
hashMap.entrySet().parallelStream().forEach( entry-> {
System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
});
测试源码
我测试的源码 也放在这里了。
public class TestForHashMap {
public static void main(String[] args) {
HashMap<Integer , String> hashMap = new HashMap<>(8);
hashMap.put(1001 , "张三");
hashMap.put(1002 , "李四");
hashMap.put(1003 , "王五");
hashMap.put(1004 , "赵六");
hashMap.put(1005 , "周七");
hashMap.put(1006 , "郑八");
//迭代器实现遍历 - 其中三个普通迭代器都可以使用此方法遍历,这里以键的形
// Iterator<Integer> iterator = hashMap.keySet().iterator();
// while (iterator.hasNext()) {
// Integer key = iterator.next();
// System.out.println("key : " + key + " , value: " + hashMap.get(key));
// }
// Iterator<Map.Entry<Integer , String>> iterator1 = hashMap.entrySet().iterator();
// while (iterator1.hasNext()) {
// Map.Entry<Integer , String> entry = iterator1.next();
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// }
//增强型for循环
// for (Integer integer: hashMap.keySet()) {
// System.out.println("key : " + integer + " , value: " + hashMap.get(integer));
// }
// for (Map.Entry<Integer , String> entry: hashMap.entrySet()) {
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// }
//forEach方法遍历
// hashMap.forEach((k,v)-> {
// System.out.println("key : " + k + " , value: " + v);
// }
// );
// hashMap.keySet().forEach(k -> {
// System.out.println("key : " + k + " , value: " + hashMap.get(k));
// }
// );
// hashMap.entrySet().forEach(entry -> {
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// }
// );
//以流的形式实现遍历
// hashMap.entrySet().stream().forEach( entry-> {
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// });
// hashMap.entrySet().stream().forEach( entry-> {
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// });
// hashMap.entrySet().parallelStream().forEach( entry-> {
// System.out.println("key : " + entry.getKey() + " , value: " + entry.getValue());
// });
hashMap.entrySet().parallelStream().forEach( key-> {
System.out.println("key : " + key + " , value: " + hashMap.get(key));
});
}
}