2016-10-22 2 views
0

String과 HashSet을 키와 값으로 사용하는 hashmap이 있습니다. 지도를 업데이트하고 값을 추가하려고합니다.Java8의 HashMap에 대해 혼동 스럽습니다.

제가 동일한 키는 회 추가되고 볼 수있는 출력에

  1. map.putIfAbsent(str.substring(i,j),new HashSet<String>).add(str); //this method gives nullpointerexception

  2. map.computeIfPresent(str.substring(i,j),(k,v)->v).add(str);

을 use- 다음 방법 중 어느 이해할 수없는 초기 값 및 갱신 된 값.

누군가 이러한 방법을 사용하는 방법을 알려주십시오.

+1

NPE의 출처는 무엇입니까? 그것은'str','map' 또는'putIfAbsent'의 리턴입니까? – 4castle

+2

javadoc을 한눈에 보면'putIfAbsent'는 그 키가 아직지도에 없을 때 null을 반환한다고합니다. add()를 호출하기 전에 그 가능성을 고려 했습니까? –

+0

왜 두 가지 방법 중 하나를 선택해야한다고 생각하십니까? 이 목적을 위해 최소한 네 가지 방법이 있지만 나열한 부적절한 것을 세지 않는 것이 좋습니다. – Holger

답변

3

가장 좋은 방법은 Map#computeIfAbsent입니다. 이렇게하면 새로운 HashSet이 불필요하게 생성되지 않고 나중에 값을 반환합니다.

map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str); 
+1

이렇게하면 문제가 해결됩니다. 'putIfAbsent'는 키가지도에 이미 존재하지 않으면 null을 반환하므로 NPE의 가능성이 있습니다. 'computeIfAbsent'는 키가 존재하지 않으면 null만을 리턴하고 전달 된 값 공급자는 생성자 호출이기 때문에 null을 리턴합니다. –

2

putIfAbsentcomputeIfPresent를 선택할 이유가 없다. 가장 중요한 것은 computeIfPresent으로 완전히 부적절한 것으로 그 이름에서 알 수 있듯이 이미 오래된 값이있을 때만 새로운 값을 계산하며 (k,v)->v은이 계산에 아무런 영향을주지 않습니다.

몇 가지 옵션

  1. containsKey, putget있다. 이 목록의 그것의 가장 비효율적가 동일한 키

    String key=str.substring(i, j); 
    if(!map.containsKey(key)) 
        map.put(key, new HashSet<>()); 
    map.get(key).add(str); 
    
  2. getput 세 해시 조회까지 통합으로하지만 이것은 가장 인기있는 사전 자바 8입니다. 첫 번째 것보다 낫지 만 여전히 두 개의 조회를 통합 할 수 있습니다.

    String key=str.substring(i, j); 
    Set<String> set=map.get(key); 
    if(set==null) 
        map.put(key, set=new HashSet<>()); 
    set.add(str); 
    
  3. putIfAbsent : 일반 Map의 경우,이 자바 8 전에 최선의 선택이었다. Java 8 이전에는이 ​​옵션을 ConcurrentMap에만 사용할 수있었습니다.

    String key=str.substring(i, j); 
    Set<String> set=new HashSet<>(), old=map.putIfAbsent(key, set); 
    (old!=null? old: set).add(str); 
    

    이것은 하나의 해시 조회를 부담하지만, 우리가 필요하지 않은 경우에도, 새로운 HashSet의 무조건 생성을 필요로한다. 여기에서 get을 먼저 수행하도록 수행하는 것이 좋습니다. 특히 을 사용할 때 get을 잠금없이 수행 할 수 있으며 이후에 더 비싼 putIfAbsent을 필요로 할 수 있습니다.

    한편, Set 값의 조작은 어떤 것도 지키지 않으므로이 구조가 스레드로부터 안전하지 않다는 점을 강조해야합니다.

  4. computeIfAbsent.이 자바 8 방법은 가장 간결하고 가장 효율적으로 작동 할 수 있습니다 : 더 이전 값이없는 경우

    map.computeIfAbsent(str.substring(i, j), k -> new HashSet<>()).add(str); 
    

    이 단지 기능을 평가하는 것, 더 오래된 없었다 경우 putIfAbsent 달리,이 방법은 새로운 값을 반환 즉, 어느 경우이든 Set을 반환하므로 직접 add을 사용할 수 있습니다. 그래도 add 작업은 Map 작업 외부에서 수행되므로 Map이 스레드 안전성이 있더라도 스레드 안전성은 없습니다. 그러나 일반적으로 Maps의 경우, 즉 스레드 안전성이 중요하지 않은 경우 이것이 가장 효율적인 변형입니다.

  5. compute. 이 Java 8 메소드는 항상 함수를 평가하며 두 가지 f}으로 사용될 수 있습니다. 첫 번째

    map.compute(str.substring(i, j), (k,v) -> v==null? new HashSet<>(): v).add(str); 
    

    computeIfAbsent의 좀 더 자세한 변형입니다. ConcurrentHashMap 경우,이 스레드 안전 업데이트 될 수 있도록 제

    map.compute(str.substring(i, j), (k,v) -> { 
        if(v==null) v=new HashSet<>(); 
        v.add(str); 
        return v; 
    }); 
    

    그래서 대신 computeIfAbsentcompute 때 스레드 유효한 유스 케이스를 보유하여, 상기 Map의 스레드 안전 정책 하에서 Set 업데이트를 수행 안전이 중요합니다.

+0

스레드 안전 멀티 맵 또한 내부 컬렉션이 스레드 안전성이 필요합니까? 그렇지 않으면'get' 연산에 의해 리턴 된 콜렉션의 상태는 맵이 저장하는 값에 래퍼를주지 않기 때문에 쓰레드에 안전하지 않을 것이다. – 4castle

+0

"thread safe multimap"구현을 염두에두고 있습니까? 일반적으로, thread 세이프 맵에 의해 건네받는 값이 변경 가능한 경우, 그 값의 thread 세이프 티를 확실히하는 방법에 관한 다른 폴리시가 필요합니다. 정책을 구현하는 컬렉션 자체 일 필요는 없지만 액세스하는 모든 스레드는 정책을 준수해야합니다. – Holger

+0

[이 방법으로 만든 새 세트] (http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#newKeySet--)를 사용한 구현을 생각하고있었습니다. 값이 동시 백업을 갖도록합니다. 그것은 좋은 지적입니다. 'compute' 또는'computeIfPresent'가 값이 변경되는 유일한 방법 인 경우에 따라 스레드 안전성이 있습니다. – 4castle

관련 문제