2

would like to know the difference on following methods, namely why two identical (identical by its logic) methods do not return the same expected result:

  1. Method: create a Map of Character and List of Words:

     void setMapping(List<String> words) {
       Map<Character, List<String>> wordsByLetter = new HashMap<>();
       for (String word : words) {
         char letter = word.toLowerCase().charAt(0);
         List<String> ref = wordsByLetter.get(letter);
         if (ref == null) {
           ref = new ArrayList<>();
           wordsByLetter.put(letter, ref);
         }
         ref.add(word);
       }
     }
    

    So in this case we make a reference to List of Strings called 'ref' and it will be updated each time we call method 'add' on it. Unfortunately, the same approach doesn't work with the second one:

2th Method: count all appearances:

void countCategories(List<String> categories) {
  Map<String, Integer> mapper = new HashMap<>();
  for (String category : categories) {
  //need object Integer either to provide reference to it and to check whether it is a null
    Integer counter = mapper.get(category);
    if (counter == null) {
      counter = 0;
      //DOESN'T WORK THE SAME WAY:
      //mapper.put(category, counter);
    }
    counter++;
    mapper.put(category, counter);
  }
}

So, my question is, why the second method doesn't work the same way as the first one, namely, why we cannot update counter in the particular Collection through object reference?

user3467471
  • 127
  • 2
  • 12

2 Answers2

2

counter is an object of the Integer class, which means it is immutable. Therefore you cannot update its value.

counter++ returns a new Integer instance and assigns it to the counter variable. Therefore, if you call mapper.put(category, counter) before counter++, the original object referenced by counter is stored in the HashMap, and your counter++ statement has no effect on the contents of the HashMap.

That's the reason why the following code doesn't work:

Integer counter = mapper.get(category);
if (counter == null) {
  counter = 0;
  mapper.put(category, counter);
}
counter++;

And instead, you have to write:

Integer counter = mapper.get(category);
if (counter == null) {
  counter = 0;
}
counter++;
mapper.put(category, counter);

The first snippet, where you store ArrayLists in the HashMap is different, since ArrayList is mutable, and calling add for an ArrayList already stored in the HashMap mutates that ArrayList.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • I didn't get *counter is an object of the Integer class, which means it is immutable*. Also, OP has already written the code, which you suggested. – Ravi Sep 24 '17 at 10:19
  • The answer was written in details here https://stackoverflow.com/q/22793616/3025545 – Kh.Taheri Sep 24 '17 at 10:20
  • @Ravi The OP asked why he has to write different code in the two snippets (i.e. why `mapper.put(category, counter);` can't be inside the if statement in the second snippet). – Eran Sep 24 '17 at 10:21
  • @Eran Ohh.. I didn't get that. :) – Ravi Sep 24 '17 at 10:23
1

The difference comes from mutability of map's value: List<String> is mutable, while Integer is immutable.

Your second code snippet can be fixed by adding an else as follows:

if (counter == null) {
    mapper.put(category, 1);
} else {
    mapper.put(category, counter+1);
}

or an equivalent conditional expression with no if

mapper.put(category, (counter != null ? counter : 0) + 1);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • @Kh.Taheri Although your link explains a closely related concept, it is not an answer to OP's question. – Sergey Kalinichenko Sep 24 '17 at 10:24
  • @dasblinkenlight Thank you for your answer, but I believe, Eran had provided much more precise answer providing explicitly, that "counter++ returns a new Integer instance and assigns it to the counter variable", cause otherwise I would provide an example that doubt the immutability of the object Integer as follows: Integer counter = null; if (counter == null) { counter = 0; } counter++; System.out.println(counter); //result = 1; – user3467471 Sep 24 '17 at 10:39