Reputation: 621
/**
* Holds cached entrySet(). Note that AbstractMap fields are used
* for keySet() and values().
*/
transient Set<Map.Entry<K,V>> entrySet;
I find a strange thing in java Hashmap. I find no code to create an instance of and manipulate entrySet in hashmap source code and AbstractMap code. But when I put key-value in hashmap, the entrySet gets the new value. It's strange.
So my question is:
how does entrySet gets the new value, and when does it get its instance?
To be more clear, I added my debug code in IDE and find before my first call of this set, it is already not null and filled with elements.
Upvotes: 2
Views: 1749
Reputation: 992
The map entry set gets it's value on first request. This is the implementation of HashMap.entrySet():
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
It is initially null (and transient) simply so safe memory when not needed.
This has not much to do with the nested status of it's classes. It's simply a lazily initialized member variable.
Upvotes: 0
Reputation: 2188
How does entrySet gets the new value, and when does it get its instance?
The instance of entrySet is created the first time Map.entrySet() is called.
We can see that in the source code of HashMap:
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);
}
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();
}
}
}
The EntrySet
class is just a set of methods that act on the internal HashMap
. Therefore, the EntrySet
holds no information, it's just a view on the data of the HashMap
.
Upvotes: 2
Reputation: 19546
This entrySet
field is set when the entrySet()
method is called. See the source code of entrySet()
in OpenJDK 8:
public Set<Map.Entry<K,V>> entrySet() {
Set<Map.Entry<K,V>> es;
return (es = entrySet) == null ? (entrySet = new EntrySet()) : es;
}
Also check the inner EntrySet
class which is directly below the method in the linked source code.
Upvotes: 2
Reputation: 147154
Map.entrySet
will just give you a view of the Map
as a Set<Map.Entry<,>>
. It doesn't copy the data structure. List.subList
is the same. Arrays.asList
should be an easy example to understand.
In Java inner classes have two (or more) this
s (assuming they are not local classes in a static context). In Python, by convention, self
is used for this
. (I haven't done that much Python.) So any methods of inner class would be hiding the outer self. You would have to use different names, and of course be explicit every time you use them. In Java, if you are not explicit, it'll pick the this
that fits (which can be weird in corner cases).
Upvotes: 1