2016-12-19 1 views
9

HashMap의 모든 키를 소문자로 바꾸고 싶습니다. (이유는 묻지 말고 그냥해야합니다). HashMap에는 수백만 개의 항목이 있습니다.소문자로 된 모든 HashMap 키

처음에는 새 맵을 만들고 소문자로 만들 맵의 항목을 반복하고 각 값을 추가한다고 생각했습니다. 이 작업은 하루에 한 번만 실행해야하므로이 작업을 수행 할 수 있다고 생각했습니다. 내 서버가 나는지도를 복사하고 있었다이 한 시간 동안 과부하 때

Map<String, Long> lowerCaseMap = new HashMap<>(myMap.size()); 
for (Map.Entry<String, Long> entry : myMap.entrySet()) { 
    lowerCaseMap.put(entry.getKey().toLowerCase(), entry.getValue()); 
} 

그러나, 일부에서 OutOfMemory 오류를 일으켰습니다.

내 질문은, 어떻게하면이 작업을 가장 작은 메모리 풋 프린트로 수행 할 수 있습니까?

소문자를 제거한 후에 각 키를 제거할까요? 새로운지도 도움말에 추가 되었습니까?

이렇게 빨리 java8 스트림을 사용할 수 있습니까? 새로운지도

에 추가 - (이 같은 예를 들어 뭔가)

Map<String, Long> lowerCaseMap = myMap.entrySet().parallelStream().collect(Collectors.toMap(entry -> entry.getKey().toLowerCase(), Map.Entry::getValue)); 

업데이트Collections.unmodifiableMap 그래서 난 후 소문자로 각 키를 제거

의 옵션이 없습니다 있다는 것

+2

당신이 처음에 낮은 맡았다 키를 삽입 할 수 없습니다 : 여기

몇 가지 유사한 질문입니까? – Eran

+2

아니요 ... API를 사용하여이지도를 가져와 - 내 코드가 아닙니다. – sestus

+0

원래 키의 대소 문자를 구분해야합니까? – davidxxx

답변

13

을 대신 HashMap를 사용하여, 당신은 대소 문자를 구별 주문으로 TreeMap 사용하여 시도 할 수 있습니다. 이것은 각 키의 소문자 버전을 만들 필요 피할 것 : 저장하고 모든 - 사용하여 값을 가져올 수 있도록이지도를 구축하면

Map<String, Long> map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); 
map.putAll(myMap); 

put()get()는 대문자와 소문자를 구별하지 않고로 동작합니다 소문자 키.키를 반복하면 원래의 대문자 형식으로 반환됩니다.

+0

정말 재미있는 접근법입니다. API를 변경하지 않는다면 맵의 다른 인스턴스화 만 필요할 것입니다. 나는 그것을 이용하려고 노력한다. – sestus

+0

그래서 이것이 일을 한 것 같습니다. 변경 사항은 간단합니다. Map 구현 만 변경되었습니다. 고마워! – sestus

3

지도를 반복하는 동안 항목을 제거 할 수 없습니다. 이 작업을 시도하면 ConcurentModificationException이 발생합니다.

성능 문제가 아니라 OutOfMemoryError 문제이므로 병렬 스트림을 사용하면 도움이되지 않습니다.

Stream API에 대한 일부 작업이 최근에 완료 되더라도 여전히 일부 지점에서 메모리에 두 개의지도가 있으므로 문제가 계속 발생합니다.

  • 가 (Java 명령 줄에서 -Xmx를 증가) 프로세스에 메모리를 보내기

    를 해결하려면, 난 단지 두 가지 방법을 보았다. 예를 들어지도의 크기를 10으로 나눈 다음 한 번에 하나의 청크를 처리하고 새 청크를 처리하기 전에 처리 된 항목을 삭제하는 등의 작업을 수행 할 수 있습니다. 이렇게하면지도에 두 배의지도가있는 대신지도의 1.1 배가됩니다. 분할 알고리즘의

, 당신은 스트림 API를 사용하여이 같은가 someting를 시도 할 수 있습니다 :

Map<String, String> toMap = new HashMap<>();    
int chunk = fromMap.size()/10; 
for(int i = 1; i<= 10; i++){ 
    //process the chunk 
    List<Entry<String, String>> subEntries = fromMap.entrySet().stream().limit(chunk) 
     .collect(Collectors.toList()); 

    for(Entry<String, String> entry : subEntries){ 
     toMap.put(entry.getKey().toLowerCase(), entry.getValue()); 
     fromMap.remove(entry.getKey()); 
    } 
} 
+2

나는 단지 @xenteros를 인용하려고합니다. 지도에 '키'키와 '키'키가있는 경우 소문자 맵만 갖는 모든 논리는 실패하고 작업은 더 이상 의미가 없으므로 최종 상태를 달성 할 수 없습니다 (만약 당신이 그 중 하나를 제거하지 않는다면) – SomeJavaGuy

+0

이 시간 동안 원본지도가 사용되지 않았다고 가정합니다. – davidxxx

+3

(업데이트 된) 질문은 원본지도가 전혀 변경 가능하지 않다는 것을 알려줍니다 (즉,'Collections.unmodifiableMap'에 의해 반환 된지도). 그래서 청크 또는 단일 항목을 제거하려고하는지 여부는 중요하지 않습니다 (반복 및 제거 동시에'Iterator.remove()'를 사용하면됩니다). – Holger

관련 문제