Programing

map.get ()을 사용할 때 java Map.containsKey () 중복을 사용하고 있습니다.

lottogame 2020. 9. 24. 08:10
반응형

map.get ()을 사용할 때 java Map.containsKey () 중복을 사용하고 있습니다.


나는 containsKey()방법을 사용하는 것을 자제 java.util.Map하고 대신 .NET의 결과에 대해 null 검사를 수행하는 것이 모범 사례 내에서 허용 가능한지 한동안 궁금해했습니다 get().

내 근거는 값을 두 번 조회하는 것이 중복 된 것처럼 보인다는 것 containsKey()입니다 get().

반면에 대부분의 표준 Map캐시 구현은 마지막 조회 캐시하거나 컴파일러가 중복성을 제거 할 수 있으며 코드의 가독성을 위해 containsKey()부분 을 유지하는 것이 바람직 할 수 있습니다.

귀하의 의견에 감사드립니다.


일부 Map 구현은 Null 값 (예 : HashMap)을 가질 수 있습니다.이 경우 get(key)반환 null되는 경우이 키와 관련된 맵에 항목이 없음을 보장하지 않습니다.

따라서지도에 키가 포함되어 있는지 알고 싶다면 Map.containsKey. 키에 매핑 된 값이 필요한 경우 Map.get(key). 이 맵이 null 값을 허용하는 경우 null 반환 값이 반드시 맵에 키에 대한 매핑이 포함되어 있지 않음을 나타내는 것은 아닙니다. 이 경우 Map.containsKey쓸모가 없으며 성능에 영향을 미칩니다. 또한지도에 대한 동시 액세스 (예 ConcurrentHashMap:)의 Map.containsKey(key)경우 테스트 한 후 를 호출하기 전에 다른 스레드에서 항목을 제거 할 가능성이 있습니다 Map.get(key).


나는 작성하는 것이 상당히 표준이라고 생각합니다.

Object value = map.get(key);
if (value != null) {
    //do something with value
}

대신에

if (map.containsKey(key)) {
    Object value = map.get(key);
    //do something with value
}

가독성이 떨어지지 않고 약간 더 효율적이므로 그렇게하지 않을 이유가 없습니다. 분명히 지도에 null이 포함될 수 있다면 두 옵션의 의미가 동일하지 않습니다 .


assylias가 지적했듯이 이것은 의미 론적 질문입니다. 일반적으로 Map.get (x) == null은 원하는 것이지만 containsKey를 사용하는 것이 중요한 경우가 있습니다.

그러한 경우 중 하나가 캐시입니다. 나는 존재하지 않는 엔티티를 자주 찾는 데이터베이스를 쿼리하는 웹 앱에서 성능 문제를 해결 한 적이 있습니다. 해당 구성 요소에 대한 캐싱 코드를 연구했을 때 cache.get (key) == null 인 경우 데이터베이스를 쿼리하고 있다는 것을 깨달았습니다. 데이터베이스가 null (엔티티를 찾을 수 없음)을 반환하면 해당 키-> null 매핑을 캐시합니다.

containsKey로 전환하면 null 값에 대한 매핑이 실제로 의미가 있기 때문에 문제가 해결되었습니다. null에 대한 키 매핑은 존재하지 않는 키와 다른 의미를 가졌습니다.


Java8 Optional을 사용하여 @assylias가 더 읽기 쉽게 대답하도록 만들 수 있습니다.

Optional.ofNullable(map.get(key)).ifPresent(value -> {
     //do something with value
};)

  • containsKey뒤에 오는 a get는 null 값이 절대 허용되지 않는다는 선제적인 경우에만 중복됩니다. null 값이 유효하지 않으면의 호출에 containsKey사소한 성능 저하가 발생하며 아래 벤치 마크에 표시된 것처럼 오버 헤드 일뿐입니다.

  • Java 8 Optional관용구- Optional.ofNullable(map.get(key)).ifPresent또는 Optional.ofNullable(map.get(key)).ifPresent-는 바닐라 널 검사와 비교하여 사소한 오버 헤드가 발생합니다.

  • A HashMapO(1)상수 테이블 조회를 TreeMap사용하는 반면 a O(log(n))조회를 사용합니다 . containsKeya로 다음 getA의 호출 할 때 관용구가 훨씬 느립니다 TreeMap.

벤치 마크

참조 https://github.com/vkarun/enum-reverse-lookup-table-jmh를

// t1
static Type lookupTreeMapNotContainsKeyThrowGet(int t) {
  if (!lookupT.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupT.get(t);
}
// t2
static Type lookupTreeMapGetThrowIfNull(int t) {
  Type type = lookupT.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// t3
static Type lookupTreeMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupT.get(t)).orElseThrow(() -> new 
      IllegalStateException("Unknown Multihash type: " + t));
}
// h1
static Type lookupHashMapNotContainsKeyThrowGet(int t) {
  if (!lookupH.containsKey(t))
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return lookupH.get(t);
}
// h2
static Type lookupHashMapGetThrowIfNull(int t) {
  Type type = lookupH.get(t);
  if (type == null)
    throw new IllegalStateException("Unknown Multihash type: " + t);
  return type;
}
// h3
static Type lookupHashMapGetOptionalOrElseThrow(int t) {
  return Optional.ofNullable(lookupH.get(t)).orElseThrow(() -> new 
    IllegalStateException("Unknown Multihash type: " + t));
}
벤치 마크 (반복) (lookupApproach) 모드 Cnt 점수 오류 단위

MultihashTypeLookupBenchmark.testLookup 1000 t1 평균 9 33.438 ± 4.514 us / op
MultihashTypeLookupBenchmark.testLookup 1000 t2 평균 9 26.986 ± 0.405 us / op
MultihashTypeLookupBenchmark.testLookup 1000 t3 평균 9 39.259 ± 1.306 us / op
MultihashTypeLookupBenchmark.testLookup 1000 h1 평균 9 18.954 ± 0.414 us / op
MultihashTypeLookupBenchmark.testLookup          1000                h2  avgt    9  15.486 ± 0.395  us/op
MultihashTypeLookupBenchmark.testLookup          1000                h3  avgt    9  16.780 ± 0.719  us/op

TreeMap source reference

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/TreeMap.java

HashMap source reference

https://github.com/openjdk-mirror/jdk7u-jdk/blob/master/src/share/classes/java/util/HashMap.java


In Java if you check the implementation

public boolean containsKey(Object key) {
    return getNode(hash(key), key) != null;
}

public V get(Object key) {
    Node<K,V> e;
    return (e = getNode(hash(key), key)) == null ? null : e.value;
}

both use getNode to retrieve the matching, where the main work gets done.

redundancy is contextual for example of if you have a dictionary stored in a hash map. When you want to retrieve the meaning of a word

doing...

if(dictionary.containsKey(word)) {
   return dictionary.get(word);
}

is redundant.

but if you want to check a word is valid or not based on the dictionary. doing...

 return dictionary.get(word) != null;

over...

 return dictionary.containsKey(word);

is redundant.

If you check HashSet implementation, which uses HashMap internally, use 'containsKey' in 'contains' method.

    public boolean contains(Object o) {
        return map.containsKey(o);
    }

참고URL : https://stackoverflow.com/questions/14601016/is-using-java-map-containskey-redundant-when-using-map-get

반응형