9

I'm trying to get the 1st to 20th term in the Fibonacci sequence using the function computeIfAbsent() in class HashMap. Here is the code:

@Test
public void getFibMap() {
    Map<Integer, Long> map = new HashMap<>();
    map.put(1, 1L);
    map.put(2, 1L);
    fib(20, map);
    map.forEach((x, y) -> System.out.printf("key : %d --> value : %d\n", x, 
        y.));
}

public Long fib(Integer n, Map<Integer, Long> map) {
    return map.computeIfAbsent(n, v -> fib(n - 1, map) + fib(n - 2, map));
}

But when the program ends, two numbers, 19 and 20, are missing, which are supposed to be calculated. Here is the console:

key : 1 --> value : 1
key : 2 --> value : 1
key : 3 --> value : 2
key : 4 --> value : 3
key : 5 --> value : 5
key : 6 --> value : 8
key : 7 --> value : 13
key : 8 --> value : 21
key : 9 --> value : 34
key : 10 --> value : 55
key : 11 --> value : 89
key : 12 --> value : 144
key : 13 --> value : 233
key : 14 --> value : 377
key : 15 --> value : 610
key : 16 --> value : 987
key : 17 --> value : 1597
key : 18 --> value : 2584        //finished here

Process finished with exit code 0

When I specify an explicit size for the map to its constructor, these two numbers are calculated. I don't know why:

@Test
public void getFibMap() {
    Map<Integer, Long> map = new HashMap<>(25);
    map.put(1, 1L);
    map.put(2, 1L);
    fib(20, map);
    map.forEach((x, y) -> System.out.printf("key : %d --> value : %d\n", x, 
        y));
}

public Long fib(Integer n, Map<Integer, Long> map) {
    return map.computeIfAbsent(n, v -> fib(n - 1, map) + fib(n - 2, map));
}

Console:

...
key : 12 --> value : 144
key : 13 --> value : 233
key : 14 --> value : 377
key : 15 --> value : 610
key : 16 --> value : 987
key : 17 --> value : 1597
key : 18 --> value : 2584
key : 19 --> value : 4181
key : 20 --> value : 6765        //finished here

Process finished with exit code 0

Please, can somebody tell me why this happens?

Boann
  • 48,794
  • 16
  • 117
  • 146
Saber
  • 331
  • 1
  • 14
  • Please read [mcve]. Instead of explaining what some code does, post all the code required to get to these results. – GhostCat Jun 16 '17 at 06:14
  • 2
    I can reproduce this. – Sweeper Jun 16 '17 at 06:16
  • 3
    @GhostCat its kind of straightforward - 6 line of code ..... – Antoniossss Jun 16 '17 at 06:17
  • This is probably due to the hashmap getting resized inside `computeIfAbsent` call which messes it up. Try changing your `new HashMap<>()` to `new HashMap<>(20)` – Misha Jun 16 '17 at 06:17
  • Similiar issue with `ConcurrentHashMap` is discussed here: https://stackoverflow.com/q/28840047/3920048 – Misha Jun 16 '17 at 06:25
  • @AxelH Quote you have mentioned is not related as there is no concurency here. Using thread-safe ConcurrentHashMap will not solve the issue but will make it even worst (deadlock). – Antoniossss Jun 16 '17 at 06:31
  • @Antoniossss I was about to delete the comment seeing that I didn't get the statement ... didn't get my coffee yet... – AxelH Jun 16 '17 at 06:32
  • 2
    @Saber - just expand the function, do not use computeIfAbsent if your mapping function has to modify map itself. – Antoniossss Jun 16 '17 at 06:34
  • @Saber use `LinkedHashMap` insteed of `HashMap` and it will work. – Antoniossss Jun 16 '17 at 06:37
  • TreeMap will work ,also .But i want to know why Hashmap does not work ,it's the most commonly used map~~~ – Saber Jun 16 '17 at 06:38
  • @Saber read the duplicate fully with the ticket in openJDK. There is a lot to process there. You probably don't take the time yet. – AxelH Jun 16 '17 at 06:39
  • @AxelH ok ,thanks~~ – Saber Jun 16 '17 at 06:39
  • At the end of `getFibMap()`, `map.size()` is `25`!!! –  Jun 16 '17 at 07:01
  • 3
    I vote to reopen this question. The linked question is about a similar problem, but it talks about `ConcurrentHashMap`, not `HashMap`. Note that the [Javadoc for `ConcurrentHashMap`](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html) explicitly states that you are not allowed to change mappings in `computeIfAbsent()` whereas the normal [`HashMap` Javadoc](https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html#computeIfAbsent-K-java.util.function.Function-) does not. So the answers to the linked question do not apply. – Axel Jun 16 '17 at 07:01
  • 2
    There already is an open [issue](https://bugs.openjdk.java.net/browse/JDK-8172951) for this. It should be fixed in JDK 9. – Axel Jun 16 '17 at 07:05
  • 1
    @Axel The answers in the duplicate question concern `HashMap` as well. – user207421 Jun 16 '17 at 07:08
  • 3
    interesting: the `Map.computIfAbsent` is working fine (copy/adapt it into the test class). Probably not that optimized as the `HashMap` one... – user85421 Jun 16 '17 at 07:11
  • and I must agree with EJP (after reading the duplicate 3 times) despite the javadoc not being very clear about it – user85421 Jun 16 '17 at 07:25

0 Answers0