-1

I don't know if the issue is caused by my version of the JDK, my version of IntelliJ or to by lack of Java complete understanding.
I was checking HashSet implementation, and I wanted to know when the field keySet is initialized. I saw the method

public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
        ks = new KeySet();
        keySet = ks;
    }
    return ks;
}

which seems to be the place where the field is set the first time, but when I put my breakpoint on the condition, ks is never null! So I decided to check at what moment it is initialized but I don't seem to find it. I simply ran the following instruction (1) step by step by using IntelliJ's Force Step Into button :

HashSet<String> set = new HashSet<>();

I get to the instruction (2) :

map = new HashMap<>();

And to the instruction (3)

 this.loadFactor = DEFAULT_LOAD_FACTOR;

After the instruction (3), just before leaving the constructor public HashMap(), if I evaluate the field keySet, it is still null.
But right after that, exactly after instruction (2), before leaving the constructor public HashSet(), if I evaluate map.keySet it is not null anymore!

What kind of sorcery is this? Am I missing something here or is it an issue with my debugger? I am using jdk-9.0.1 and IntelliJ IDEA 2017.2.5

Ricola
  • 2,621
  • 12
  • 22

1 Answers1

3

When you debug, the debugger will try to show you the content of the HashSet, so it calls the sets toString() method, which indirectly calls the keySet() method of the backing HashMap object, thereby initializing the keySet field.

The debugger ignores breakpoints while it's making a call to toString(), so you don't see the call.

Andreas
  • 154,647
  • 11
  • 152
  • 247
  • You're right, I didn't think of checking the implementation of the `toString()` method. I updated IntelliJ to 2018.3.2 because I thought that it was a bug and with the new version it tells me explicitly : *Skipped breakpoint at ... because it happened inside debugger evaluation*. It all makes sense now because can only call `toString()` once the object has been fully created, so at the end of the constructor. However I have two more questions : – Ricola Dec 31 '18 at 10:17
  • Why does it call `toString()` although it never use the result? In the debugger, instead of showing the result of `toString()`, it only shows *size = 0* instead of *{}*. I don't find the place where it uses it. – Ricola Dec 31 '18 at 10:20
  • Right after the instruction (3), so when the `HashMap` has been created, if I evaluate `this.toString()`, it should create the field `keySet` (since it also call `keySet()` )but if I evaluate the field right after that, it is still `null`. So why does it work with the `HashSet`but not with the `HashMap`? – Ricola Dec 31 '18 at 10:25
  • I think I have the answer for the first question : It seems that *view as Collection* call `toString()`. To prevent the `toString()` evaluation of the `Set`, I had to disable **both** *Enable toString()* and *Enable alternative view for Collection classes* in the [Settings](https://stackoverflow.com/questions/11189708/is-it-possible-to-tell-intellij-idea-to-automatically-invoke-tostring-on-the-o) – Ricola Dec 31 '18 at 10:51
  • 1
    Map's `toString()` doesn't call `keySet()`. Set's `toString()` calls `iterator()`, which delegates to its maps `keySet().iterator()`. – Andreas Dec 31 '18 at 19:34
  • You're right, sorry I mixed up `keySet ` and `entrySet`. – Ricola Jan 01 '19 at 16:05