Reputation: 41
When I read source code of JDK, I found something quite confused me. That is how does the HashMap track entrySet or values.
Here is the clear() code of HashMap
public void clear() {
Node<K,V>[] tab;
modCount++;
if ((tab = table) != null && size > 0) {
size = 0;
for (int i = 0; i < tab.length; ++i)
tab[i] = null;
}
}
}
I don't know how does this method control the entrySet. As I know, entrySet is cached by the instance of HashMap, but I never found anywhere it changes.
The code upon there is only clear the table. Here is some fields of HashMap.
public class HashMap<K,V> extends AbstractMap<K,V>
implements Map<K,V>, Cloneable, Serializable {
transient Node<K, V>[] table;
transient Set<Map.Entry<K, V>> entrySet;
transient size;
transient int modCount;
int threshold;
final float loadFactor;
// other part
}
I don't understand here. Since clear method only changes the table, how does it effects the entrySet.
I use JUnit to test my thinking. The HashMap's entrySet is empty after I call the clear() method.
Here is the entrySet() code below
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
if entrySet is not null, it returns the cached entrySet, but clear() method never changes cached entrySet. How does it work?
Upvotes: 4
Views: 221
Reputation: 37845
entrySet()
is just a view. There's nothing in it, except (implicitly) a reference to the HashMap
. It doesn't create a second data structure, the Set
methods just access the map directly.
See the private HashMap.EntrySet
class for the way it's implemented. It's an inner class, so it can see the enclosing HashMap
instance (the object that "owns" it).
Here's a very simple example which works exactly the same way:
interface Foo {
void setFizz(int fizz);
int getFizz();
}
interface Bar {
void setBazz(int bazz);
int getBazz();
// returns a view of this Bar as if it were a Foo
Foo asFoo();
}
class BarImpl implements Bar {
int bazz;
@Override
public int setBazz(int bazz) {
this.bazz = bazz;
}
@Override
public int getBazz() {
return this.bazz;
}
@Override
public Foo asFoo() {
return new Foo() {
@Override
public int setFizz(int fizz) {
BarImpl.this.bazz = fizz;
}
@Override
public int getFizz() {
return BarImpl.this.bazz;
}
};
}
}
There is only ever a single int
, calling asFoo()
just lets us access the Bar
as if it were a Foo
.
Upvotes: 3