질문 : Java에서 Map 값을 증가시키는 가장 효율적인 방법
이 질문이이 포럼에서 너무 기본적인 것으로 간주되지 않기를 바랍니다. 그러나 우리는 보게 될 것입니다. 여러 번 실행되는 더 나은 성능을 위해 일부 코드를 리팩터링하는 방법이 궁금합니다.
Map (아마도 HashMap)을 사용하여 단어 빈도 목록을 만들고 있다고 가정 해 보겠습니다. 여기서 각 키는 계산되는 단어가있는 문자열이고 값은 단어의 토큰이 발견 될 때마다 증가하는 정수입니다.
Perl에서 이러한 값을 증가시키는 것은 간단합니다.
$map{$word}++;
그러나 Java에서는 훨씬 더 복잡합니다. 여기 내가 현재하고있는 방식 :
int count = map.containsKey(word) ? map.get(word) : 0;
map.put(word, count + 1);
물론 최신 Java 버전의 자동 박싱 기능에 의존합니다. 이러한 값을 증가시키는보다 효율적인 방법을 제안 할 수 있는지 궁금합니다. Collections 프레임 워크를 피하고 대신 다른 것을 사용하는 좋은 성능 이유가 있습니까?
업데이트 : 몇 가지 답변을 테스트했습니다. 아래를 참조하십시오.
답변
일부 테스트 결과
저는 이 질문에 대한 많은 좋은 답을 얻었습니다 – 여러분 감사합니다 – 그래서 저는 몇 가지 테스트를 실행하고 어떤 방법이 실제로 가장 빠른지 알아 내기로 결정했습니다. 내가 테스트 한 다섯 가지 방법은 다음과 같습니다.
- 질문 에서 제시 한 "ContainsKey"메소드
- Aleksandar Dimitrov가 제안한 "TestForNull"메서드
- 행크 게이가 제안한 "AtomicLong"방법
- jrudolph가 제안한 "Trove"방법
- phax.myopenid.com에서 제안한 "MutableInt"메소드
메서드
내가 한 일은 ...
- 아래 표시된 차이점을 제외하고는 동일한 5개의 클래스를 생성했습니다. 각 클래스는 내가 제시한 시나리오의 일반적인 작업을 수행해야 했습니다. 10MB 파일을 열고 읽은 다음 파일에 있는 모든 단어 토큰의 빈도 계산을 수행했습니다. 평균 3초 밖에 걸리지 않았기 때문에 I/O가 아닌 주파수 카운트를 10번 수행했습니다.
- I/O 작업이 아닌 10회 반복의 루프 시간을 측정하고 기본적으로 Java Cookbook에서 lan Darwin의 방법을 사용하여 소요된 총 시간(시계 초 단위)을 기록했습니다.
- 다섯 번의 테스트를 모두 직렬로 수행한 다음 세 번 더 수행했습니다.
- 각 방법에 대한 4개의 결과를 평균화했습니다.
결과
관심있는 사람들을 위해 먼저 결과와 아래 코드를 제시하겠습니다.
ContainsKey 메서드는 예상대로 가장 느렸으므로 해당 메서드의 속도와 비교하여 각 메서드의 속도를 제공하겠습니다.
- ContainsKey : 30.654 초 (기준)
- AtomicLong : 29.780 초 (1.03 배 빠름)
- TestForNull : 28.804 초 (1.06 배 빠름)
- 트루 브 : 26.313 초 (1.16 배 빠름)
- MutableInt : 25.747 초 (1.19 배 빠름)
결론
MutableInt 메서드와 Trove 메서드 만 10 % 이상의 성능 향상을 제공한다는 점에서 훨씬 더 빠릅니다. 그러나 스레딩이 문제라면 AtomicLong이 다른 것보다 더 매력적일 수 있습니다 (정말 잘 모르겠습니다). final
변수로 TestForNull을 실행했지만 그 차이는 미미했습니다.
다른 시나리오에서 메모리 사용량을 프로파일 링하지 않았습니다. MutableInt 및 Trove 메서드가 메모리 사용에 어떻게 영향을 미칠 수 있는지에 대한 좋은 통찰력을 가진 사람의 의견을 듣고 기쁩니다.
개인적으로 MutableInt 메서드가 가장 매력적이라고 생각합니다. 타사 클래스를로드 할 필요가 없기 때문입니다. 그래서 내가 그것에 문제를 발견하지 않는 한, 내가 갈 가능성이 가장 높은 방법입니다.
코드
다음은 각 방법의 중요한 코드입니다.
ContainsKey
import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
int count = freq.containsKey(word) ? freq.get(word) : 0;
freq.put(word, count + 1);
TestForNull
import java.util.HashMap;
import java.util.Map;
...
Map<String, Integer> freq = new HashMap<String, Integer>();
...
Integer count = freq.get(word);
if (count == null) {
freq.put(word, 1);
}
else {
freq.put(word, count + 1);
}
AtomicLong
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicLong;
...
final ConcurrentMap<String, AtomicLong> map =
new ConcurrentHashMap<String, AtomicLong>();
...
map.putIfAbsent(word, new AtomicLong(0));
map.get(word).incrementAndGet();
Trove
import gnu.trove.TObjectIntHashMap;
...
TObjectIntHashMap<String> freq = new TObjectIntHashMap<String>();
...
freq.adjustOrPutValue(word, 1, 1);
MutableInt
import java.util.HashMap;
import java.util.Map;
...
class MutableInt {
int value = 1; // note that we start at 1 since we're counting
public void increment () { ++value; }
public int get () { return value; }
}
...
Map<String, MutableInt> freq = new HashMap<String, MutableInt>();
...
MutableInt count = freq.get(word);
if (count == null) {
freq.put(word, new MutableInt());
}
else {
count.increment();
}
출처 : https://stackoverflow.com/questions/81346/most-efficient-way-to-increment-a-map-value-in-java
'프로그래밍 언어 > JAVA' 카테고리의 다른 글
자바 : List<String> 문자열로 변환 (0) | 2021.11.30 |
---|---|
Java에서 디렉토리를 만드는 방법 (0) | 2021.11.29 |
자바 HashMap을 통해 반복하는 방법 (0) | 2021.11.26 |
Try / Catch를 사용하지 않고 JavaScript에서 문자열이 유효한 JSON 문자열인지 확인하는 방법 (0) | 2021.11.26 |
java.time.LocalDateTime과 java.util.Date 간 변환 (0) | 2021.11.24 |