обзор
В библиотеках Java 8 появился новый метод map, computeIfAbsent. Это очень полезный способ превратить вашу Карту в кеш объектов, связанных с ключом.
Однако есть комбинация, которую вы, возможно, не рассматривали; что произойдет, если вы вызовете computeIfAbsent внутри себя.
|
1
2
3
4
5
6
|
map.computeIfAbsent(Key.Hello, s -> { map.computeIfAbsent(Key.Hello, t -> 1); return 2;});enum Key {Hello} |
Хотя в простых случаях это может показаться странным, но в более сложном коде вы можете сделать это случайно (как я сделал сегодня днем). Так что же происходит? Ну, это зависит от коллекции, которую вы используете.
|
1
2
3
4
5
6
7
8
9
|
HashMap: {Hello=2}WeakHashMap: {Hello=2}TreeMap: {Hello=2}IdentityHashMap: {Hello=2}EnumMap: {Hello=2}Hashtable: {Hello=2, Hello=1}LinkedHashMap: {Hello=1, Hello=2}ConcurrentSkipListMap: {Hello=1}ConcurrentHashMap: |
Примечание: ConcurrentHashMap никогда не возвращается. Это блокировка не кажется повторной.
ConcurrentSkipListMap имеет наиболее разумный результат, сохраняя первую добавленную стоимость. Hello = 2 подходит для этой неопределенной ситуации, если сбивает с толку, поскольку это второе значение, а не первое. Что не имеет особого смысла, так это чтобы дважды появился уникальный неизменяемый ключ.
Наличие тупика ConcurrentHashMap само по себе является неудачным, но, по крайней мере, не очень тонким.
Полный код
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class A { public static void main(String[] args) { for (Map map : new Map[]{ new HashMap<>(), new WeakHashMap<>(), new TreeMap<>(), new IdentityHashMap<>(), new EnumMap<>(Key.class), new Hashtable<>(), new LinkedHashMap<>(), new ConcurrentSkipListMap<>(), new ConcurrentHashMap<>() }) { System.out.print(map.getClass().getSimpleName() + ": "); map.computeIfAbsent(Key.Hello, s -> { map.computeIfAbsent(Key.Hello, t -> 1); return 2; }); System.out.println(map); } } enum Key {Hello}} |
Метод compute () имеет аналогичные результаты
|
1
2
3
4
5
6
7
8
|
HashMap: {Hello=null2}WeakHashMap: {Hello=null2}TreeMap: {Hello=null2}IdentityHashMap: {Hello=null2}EnumMap: {Hello=null2}Hashtable: {Hello=null2, Hello=1}LinkedHashMap: {Hello=1, Hello=null2}ConcurrentSkipListMap: {Hello=12} |
ConcurrentHashMap:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
public class A { public static void main(String[] args) { for (Map map : new Map[]{ new HashMap<>(), new WeakHashMap<>(), new TreeMap<>(), new IdentityHashMap<>(), new EnumMap<>(Key.class), new Hashtable<>(), new LinkedHashMap<>(), new ConcurrentSkipListMap<>(), new ConcurrentHashMap<>() }) { System.out.print(map.getClass().getSimpleName() + ": "); map.compute(Key.Hello, (s, v) -> { map.compute(Key.Hello, (s2, v2) -> "1"); return v + "2"; }); System.out.println(map); } } enum Key {Hello}} |
Вывод
Вы должны быть осторожны, если вы вкладываете вызовы в карту из лямбды, или вообще избегаете этого. Если вам нужно сделать это, ConcurrentSkipListMap, кажется, ведет себя лучше всего.
| Ссылка: | Puzzler: вложенный computeIfAbsent от нашего партнера JCG Питера Лори в блоге Vanilla Java . |