java hashmap 迭代器_Java8 HashMap的迭代器和转化Set的实现

本文详细介绍了Java HashMap的迭代器实现,包括HashIterator、KeyIterator、ValueIterator和EntryIterator,以及它们在遍历哈希表时的逻辑。此外,还讲解了HashMap如何转化成KeySet、EntrySet和Values,以及这些Set结构的特点和使用方式。
摘要由CSDN通过智能技术生成

相关迭代器

Java HashMap集成哈希表和Map两种结构,迭代器需要遍历哈希表的每个元素(每个元素是一个key-value对),也需要遍历Map中的每个key或者每个value。在迭代器设计中引入了两种类型和每种类型包含三种类型的迭代器,共6个迭代器提供开发者使用,2个内部abstract迭代器提供公共逻辑。

HashIterator

遍历示意图

先遍历相同桶中的元素,再遍历下一个桶,返回非空的元素

a77c90792223

HashIterator遍历示意图

代码解析

有了上面的遍历示意图,下面的代码理解起来就比较容易了。在代码中注释了关键操作。

abstract class HashIterator {

Node next;

Node current;

int expectedModCount;

int index;

/*构造函数初始化current指向null,next指向哈希表中第一个非空的元素*/

HashIterator() {

expectedModCount = modCount;

Node[] 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 nextNode() {

Node[] t;

Node 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 p = current;

if (p == null)

throw new IllegalStateException();

if (modCount != expectedModCount)

throw new ConcurrentModificationException();

current = null;

K key = p.key;

/*调用HashMap的removeNode函数删除结点*/

removeNode(hash(key), key, null, false, false);

expectedModCount = modCount;

}

}

KeyIterator,ValueIterator和EntryIterator

实现迭代器接口的next()方法,根据需要返回Entry结点,或者key值,或者value值。

final class KeyIterator extends HashIterator implements Iterator {

public final K next() { return nextNode().key; }

}

final class ValueIterator extends HashIterator implements Iterator {

public final V next() { return nextNode().value; }

}

final class EntryIterator extends HashIterator implements Iterator> {

public final Map.Entry next() { return nextNode(); }

}

Spliterators相关类

Java8新增的Spliterators相关的类,包含HashSpIiterator,KeySpIiterator,ValueSpIiterator和EntrySpIiterator。遍历与上面的迭代器遍历示意图一致,HashMap被分段访问与List原理一致。这里不赘述,可参阅Java的LinkedList源码解析或者Java的ArrayList源码解析的Spliterators讲解。

转化Set的相关类

转化Set的相关类为开发者提供了更加方便的访问HashMap元素的功能,不需要使用和获取迭代器,仅需调用***Set()方法返回集合类型,利用foreach循环即可访问元素。比起获取迭代器更加常用也更加方便。

重点提示:调用生成set的方法均在第一次调用时初始化set实例——延迟初始化,然后赋值给类中变量保存,换言之每次调用都返回同样的set实例。而且set实例相当于HashMap实例的一个视图,当set实例中元素发生变化,HashMap中元素同样改变。

KeySet

public Set keySet() {

Set ks;

return (ks = keySet) == null ? (keySet = new KeySet()) : ks;

}

/*KeySet继承AbstractSet抽象类,是一个set结构,调用HashMap函数完成特定操作*/

final class KeySet extends AbstractSet {

public final int size() { return size; }

public final void clear() { HashMap.this.clear(); }

public final Iterator 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 spliterator() {

return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);

}

public final void forEach(Consumer super K> action) {

Node[] 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 e = tab[i]; e != null; e = e.next)

action.accept(e.key);

}

if (modCount != mc)

throw new ConcurrentModificationException();

}

}

}

EntrySet

public Set> entrySet() {

Set> es;

return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;

}

/*EntrySet是一个set结构,同样调用HashMap函数实现指定功能*/

final class EntrySet extends AbstractSet> {

public final int size() { return size; }

public final void clear() { HashMap.this.clear(); }

public final Iterator> 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 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> spliterator() {

return new EntrySpliterator<>(HashMap.this, 0, -1, 0, 0);

}

public final void forEach(Consumer super Map.Entry> action) {

Node[] 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 e = tab[i]; e != null; e = e.next)

action.accept(e);

}

if (modCount != mc)

throw new ConcurrentModificationException();

}

}

}

Values

public Collection values() {

Collection vs;

return (vs = values) == null ? (values = new Values()) : vs;

}

/*Values继承AbstractCollection抽象类,是一个Collection结构,调用HashMap的函数完成特定操作*/

final class Values extends AbstractCollection {

public final int size() { return size; }

public final void clear() { HashMap.this.clear(); }

public final Iterator iterator() { return new ValueIterator(); }

public final boolean contains(Object o) { return containsValue(o); }

public final Spliterator spliterator() {

return new ValueSpliterator<>(HashMap.this, 0, -1, 0, 0);

}

public final void forEach(Consumer super V> action) {

Node[] 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 e = tab[i]; e != null; e = e.next)

action.accept(e.value);

}

if (modCount != mc)

throw new ConcurrentModificationException();

}

}

}

总结

HashMap的迭代器和转化Set的实现是在遍历时经常用到的两个功能。掌握了解图示中遍历的顺序并且学会如何遍历HashMap中的元素,在阅读了HashMap函数的源代码之后,参看链接Java的HashMap源码解析,对于本节的源码自然了然于胸。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值