해시 맵의 키가 주어진 값을 업데이트하는 방법은 무엇입니까?


624

HashMap<String, Integer>Java 가 있다고 가정하십시오 .

찾은 문자열이있을 때마다 문자열 키의 정수 값을 어떻게 업데이트 (증가)합니까?

한 쌍을 제거하고 다시 입력 할 수 있지만 오버 헤드가 문제가됩니다.
또 다른 방법은 새 페어를 넣고 기존 페어를 교체하는 것입니다.

후자의 경우 삽입하려는 새 키와의 해시 코드 충돌이 발생하면 어떻게됩니까? 해시 테이블의 올바른 동작은 다른 위치를 지정하거나 현재 버킷에서 목록을 작성하는 것입니다.

답변:


972
map.put(key, map.get(key) + 1);

괜찮을거야. 기존 매핑의 값을 업데이트합니다. 이것은 자동 복싱을 사용합니다. 의 도움으로 map.get(key)우리는 키를 해당 값을 얻을, 당신은 당신의 요구 사항을 업데이트 할 수 있습니다. 여기에서는 값을 1 씩 늘리도록 업데이트하고 있습니다.


21
실제로 이것이 가장 강력하고 확장 가능한 엔터프라이즈 솔루션입니다.
Whiolet Lavir

12
@Lavir는 나쁜 해결책은 아니지만 가장 강력하고 확장 가능한 방법을 보지 못합니다. 아토믹 인티 저는 대신 훨씬 더 확장 가능합니다.
John Vint

13
이것은 키가 존재한다고 가정합니다. 그렇지 않으면 nullPointer 예외가 발생합니다.
Ian

84
Java 8에서는 다음과 같이를 사용하여 쉽게 피할 수 있습니다 getOrDefault.map.put(key, count.getOrDefault(key, 0) + 1);
Martin

2
@Martin .. map.put (key, map.getOrDefault (key, 0) + 1)
Sathesh

112

자바 8 방법 :

computeIfPresent메소드 를 사용 하여 맵핑 함수를 제공 할 수 있으며, 이는 기존 값을 기반으로 새 값을 계산하기 위해 호출됩니다.

예를 들어

Map<String, Integer> words = new HashMap<>();
words.put("hello", 3);
words.put("world", 4);
words.computeIfPresent("hello", (k, v) -> v + 1);
System.out.println(words.get("hello"));

또는 merge방법을 사용할 수 있습니다 . 여기서 1은 기본값이며 함수는 기존 값을 1 씩 증가시킵니다.

words.merge("hello", 1, Integer::sum);

또한, 같은 다른 유용한 방법의 무리가 putIfAbsent, getOrDefault, forEach


3
방금 솔루션을 테스트했습니다. 두 번째 방법은 메서드 참조를 사용하는 것입니다. 첫 번째, 람다 표현식 하나는, 맵의 값이 때 일관되게 작동하지 않는 경우 null(예를 들어 words.put("hello", null);), 결과는 아직 null없는 1나는 기대한다.
Tao Zhang

4
Javadoc에서 : "지정된 키의 값이 존재하고 널이 아닌 경우 새 맵핑 계산을 시도합니다". compute()대신 사용할 수 있으며 null값도 처리 합니다.
damluar

나는 1 씩 내 값을 증가 싶은 .merge내 솔루션입니다 Integer::sum.
S_K

48
hashmap.put(key, hashmap.get(key) + 1);

이 메소드 put는 기존 키의 값을 대체 하고 존재하지 않는 경우이를 작성합니다.


55
아니요, 만들지 않습니다 nullPointer Exception.
smttsp

13
코드는 주어진 질문에 대한 정답이지만, 동일한 코드가 수락 된 답변에 게시 된 후 1 년 후에 게시되었습니다. 이 답변을 차별화하는 것은 put이 새로운 항목을 만들 수 있다는 것입니다.이 예제에서는 가능하지 않습니다. 존재하지 않는 키 / 값에 hashmap.get (key)를 사용하면 @smttsp가 NPE라고 말한 것처럼 증가하려고 할 때 null이됩니다. -1
Zach

8
이 답변은 잘못되었습니다. 존재하지 않는 키에 대한 NullPointerException
Eleanore

@smttp NullpointterException 값을 초기화하지 않은 경우에만 (널을 증가시킬 수 없음)
Mehdi

중복 및 잘못된 설명 ... 및 존재하지 않는 경우 생성합니다.null + 1 이렇게하면 null정수로 unboxe 하여 증가를 수행 할 수 없으므로 수행 할 수 없습니다 .
AxelH


30

교체 IntegerAtomicInteger와 한 전화 incrementAndGet/ getAndIncrement그것의 방법을.

대안은 메소드 가있는 int자신의 MutableInteger클래스 를 감싸는 것 입니다 increment(). 아직 스레드 안전 문제 만 해결해야합니다.


37
AtomicInteger는 가변 정수이지만 내장되어 있습니다. 나는 자신의 MutableInteger를 작성하는 것이 더 좋은 아이디어라는 것을 진지하게 의심합니다.
Peter Lawrey

오버 헤드가있는 MutableIntegerAtomicInteger사용 volatile하면 사용자 정의 가 더 좋습니다 . int[1]대신에 사용하고 싶습니다 MutableInteger.
Oliv

@Oliv : 동시 적이 지 않습니다.
BalusC

@BalusC이지만 여전히 휘발성 쓰기가 더 비쌉니다. 캐시를 무효화합니다. 차이가 없다면 모든 변수는 변동성이 있습니다.
Oliv

@Oliv : 질문에 명시 적으로 해시 코드 충돌이 언급되므로 OP에 동시성이 중요합니다.
BalusC

19

한 줄 솔루션 :

map.put(key, map.containsKey(key) ? map.get(key) + 1 : 1);

4
그것은 기존 답변에 새로운 것을 추가하지 않습니까?
Robert

1
예, 그렇습니다. 키가 존재하지 않으면 표시된 응답이 NullPointerException을 발생시킵니다. 이 솔루션은 잘 작동합니다.
Hemant Nagpal

18

@Matthew의 솔루션은 가장 단순하며 대부분의 경우 성능이 우수합니다.

고성능이 필요한 경우 AtomicInteger는 @BalusC보다 나은 솔루션입니다.

그러나 빠른 스레드 솔루션 (제공된 스레드 안전성은 문제가되지 않음)은 AtomicInteger를 작성하는 것보다 증가 (키) 메소드를 제공하고 기본 요소와 적은 오브젝트 를 사용하는 TObjectIntHashMap 을 사용 하는 것입니다. 예 :

TObjectIntHashMap<String> map = new TObjectIntHashMap<String>()
map.increment("aaa");

13

아래와 같이 증가시킬 수 있지만 NullPointerException이 발생하지 않도록 존재를 확인해야합니다.

if(!map.containsKey(key)) {
 p.put(key,1);
}
else {
 p.put(key, map.getKey()+1);
}

9

해시가 존재하거나 (값이 0 임) 첫 번째 증분에서 맵에 "넣어"있습니까? 첫 번째 증분에 "입력"되면 코드는 다음과 같아야합니다.

if (hashmap.containsKey(key)) {
    hashmap.put(key, hashmap.get(key)+1);
} else { 
    hashmap.put(key,1);
}

7

조금 늦었을지 모르지만 여기에는 2 센트가 있습니다.

Java 8을 사용하는 경우 computeIfPresent 메소드를 사용할 수 있습니다 . 지정된 키의 값이 존재하고 널이 아닌 경우 키와 현재 맵핑 된 값이 주어지면 새 맵핑을 계산하려고 시도합니다.

final Map<String,Integer> map1 = new HashMap<>();
map1.put("A",0);
map1.put("B",0);
map1.computeIfPresent("B",(k,v)->v+1);  //[A=0, B=1]

putIfAbsent 메소드 를 사용 하여 키를 넣을 수도 있습니다. 지정된 키가 값과 아직 연결되어 있지 않거나 null에 매핑 된 경우이 메서드는 해당 키를 지정된 값과 연결하고 null을 반환하고, 그렇지 않으면 현재 값을 반환합니다.

맵이 스레드간에 공유되는 경우 ConcurrentHashMapAtomicInteger를 사용할 수 있습니다 . 문서에서 :

A AtomicInteger는 원자 적으로 업데이트 될 수있는 int 값입니다. AtomicInteger는 원자 단위로 증가 된 카운터와 같은 응용 프로그램에서 사용되며 Integer를 대체 할 수 없습니다. 그러나이 클래스는 숫자 기반 클래스를 처리하는 도구 및 유틸리티로 균일하게 액세스 할 수 있도록 Number를 확장합니다.

다음과 같이 사용할 수 있습니다.

final Map<String,AtomicInteger> map2 = new ConcurrentHashMap<>();
map2.putIfAbsent("A",new AtomicInteger(0));
map2.putIfAbsent("B",new AtomicInteger(0)); //[A=0, B=0]
map2.get("B").incrementAndGet();    //[A=0, B=1]

관찰해야 할 한 가지 점은 우리가 get키의 가치를 얻기 위해 B호출 한 다음 incrementAndGet()물론 그 가치에 대해 호출 한다는 것입니다 AtomicInteger. 메소드 putIfAbsent가 이미 존재하는 경우 키 값을 리턴하므로 이를 최적화 할 수 있습니다 .

map2.putIfAbsent("B",new AtomicInteger(0)).incrementAndGet();//[A=0, B=2]

참고로 AtomicLong 을 사용하려는 경우 경쟁이 많은 문서에서 LongAdder의 예상 처리량은 공간 소비를 높이면서 상당히 높아집니다. 이 질문 도 확인하십시오 .


5

NullPointerException이없는 클리너 솔루션은 다음과 같습니다.

map.replace(key, map.get(key) + 1);

5
키가 존재하지 않으면 map.get (key)는 NPE를 발생시킵니다
Navi

네 맞습니다
Sergey Dirin

2

평판이 낮아서 몇 가지 답변에 대해서는 언급 할 수 없으므로 내가 적용한 솔루션을 게시 할 것입니다.

for(String key : someArray)
{
   if(hashMap.containsKey(key)//will check if a particular key exist or not 
   {
      hashMap.put(hashMap.get(key),value+1);// increment the value by 1 to an already existing key
   }
   else
   {
      hashMap.put(key,value);// make a new entry into the hashmap
   }
}

1

for루프를 사용 하여 색인을 늘리십시오.

for (int i =0; i<5; i++){
    HashMap<String, Integer> map = new HashMap<String, Integer>();
    map.put("beer", 100);

    int beer = map.get("beer")+i;
    System.out.println("beer " + beer);
    System.out ....

}

3
각 반복마다 맵을 덮어 씁니다. 올바른 접근 방법은 Matthew의 답변을 참조하십시오.
Leigh


1
Integer i = map.get(key);
if(i == null)
   i = (aValue)
map.put(key, i + 1);

또는

Integer i = map.get(key);
map.put(key, i == null ? newValue : i + 1);

정수는 기본 데이터 유형 http://cs.fit.edu/~ryan/java/language/java-data.html 이므로 가져 와서 일부 프로세스를 작성한 후 다시 넣어야합니다. 기본 데이터 유형이 아닌 값이있는 경우이를 가져 와서 처리하고 해시 맵에 다시 넣을 필요가 없습니다.


1
이 코드 스 니펫에 감사드립니다. 즉각적인 도움이 될 수 있습니다. 적절한 설명 이것이 문제에 대한 좋은 해결책 인지 보여줌으로써 교육적 가치를 크게 향상시킬 것이며 , 유사하지만 동일하지 않은 질문을 가진 미래 독자들에게 더 유용 할 것입니다. 제발 편집 설명을 추가하고, 제한 및 가정이 적용 무엇의 표시를 제공하는 답변을.
Toby Speight

0

시험:

HashMap hm=new HashMap<String ,Double >();

노트:

String->give the new value; //THIS IS THE KEY
else
Double->pass new value; //THIS IS THE VALUE

해시 맵에서 키 또는 값을 변경할 수 있지만 동시에 둘 다 변경할 수는 없습니다.


0

기능 'computeIfPresent'에 내장 된 Java8 사용

예:

public class ExampleToUpdateMapValue {

    public static void main(String[] args) {
        Map<String,String> bookAuthors = new TreeMap<>();
        bookAuthors.put("Genesis","Moses");
        bookAuthors.put("Joshua","Joshua");
        bookAuthors.put("Judges","Samuel");

        System.out.println("---------------------Before----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
        // To update the existing value using Java 8
        bookAuthors.computeIfPresent("Judges", (k,v) -> v = "Samuel/Nathan/Gad");

        System.out.println("---------------------After----------------------");
        bookAuthors.entrySet().stream().forEach(System.out::println);
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.