final class keyset extends abstractset<k> {
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);
}
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();
}
}
}
final class keyiterator extends hashiterator
implements iterator<k> {
public final k next() { return nextnode().key; }
}
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;
}
}
那么,这里我们可以思考这么一个问题。通过hashmap的keyset获取到keyset后,难道只能用迭代器遍历吗?keyset方法不把hashmap的key都加入到set中,那么调用者使用for(int i = 0; i < size; i ++)的方式遍历时,岂不是无法遍历set中的key了吗?是的,确实是的。keyset确实没有把key加入到set中,另外,它不用担心调用者用for(int i = 0; i < size; i ++)的方式遍历时获取不到key,因为set根本就没有set.get(i)这样类似的方法,要想遍历set,只能用迭代器,或者使用foreach方式(本质还是迭代器)。
这里还有个问题需要解释,就是在调试代码时,既然key没有加入到set中,那么ide如何显示出set中有2个元素这样的信息的?原来,ide显示对象信息时,会调用对象的tostring方法。而集合的tostring方法就是显示出集合中的元素个数。
这里再思考一步,如果我们在集合的tostring方法加上断点,那么ide显示对象信息时,会不先停下来?答案是看情况。记得早些年间使用eclipse调试代码时,在tostring方法加上断点后,显示对象信息时确实会停下来。然而我现在使用的是ide是idea,idea在这一点上做了优化。如果是ide显示对象信息调用的tostring方法,那么tostring方法的断点会被跳过,即不生效,但会给出一条提示信息,如下图。如果程序员主动调用对象的tostring方法,那么,tostring方法的断点会生效,可以正常断点调试。