다음과 같은 이유로 대소 문자를 구분하지 않는 문자열을 HashMap 키로 사용하고 싶습니다.
- 초기화하는 동안 내 프로그램은 사용자 정의 문자열로 HashMap을 만듭니다.
- 이벤트 (내 경우에는 네트워크 트래픽)를 처리하는 동안 다른 경우에는 String을 받았을 수도 있지만
<key, value>
트래픽에서받은 사례를 무시하고 HashMap에서 from 을 찾을 수 있어야 합니다.
나는이 접근법을 따랐다.
CaseInsensitiveString.java
public final class CaseInsensitiveString {
private String s;
public CaseInsensitiveString(String s) {
if (s == null)
throw new NullPointerException();
this.s = s;
}
public boolean equals(Object o) {
return o instanceof CaseInsensitiveString &&
((CaseInsensitiveString)o).s.equalsIgnoreCase(s);
}
private volatile int hashCode = 0;
public int hashCode() {
if (hashCode == 0)
hashCode = s.toUpperCase().hashCode();
return hashCode;
}
public String toString() {
return s;
}
}
LookupCode.java
node = nodeMap.get(new CaseInsensitiveString(stringFromEvent.toString()));
이 때문에 모든 이벤트에 대해 CaseInsensitiveString의 새 객체를 만들고 있습니다. 따라서 성능이 저하 될 수 있습니다.
이 문제를 해결하는 다른 방법이 있습니까?
답변
Map<String, String> nodeMap =
new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
그게 당신이 필요한 전부입니다.
답변
Guido García가 제안한대로 여기 에 대한 답변 :
import java.util.HashMap;
public class CaseInsensitiveMap extends HashMap<String, String> {
@Override
public String put(String key, String value) {
return super.put(key.toLowerCase(), value);
}
// not @Override because that would require the key parameter to be of type Object
public String get(String key) {
return super.get(key.toLowerCase());
}
}
또는
답변
한 가지 방법은 Apache Commons AbstractHashedMap
클래스 의 사용자 정의 서브 클래스를 작성하여 대소 문자를 구분하지 않는 해시 및 키 비교를 수행하기 위해 hash
및 isEqualKeys
메소드를 대체하는 것입니다. (참고-나는 이것을 직접 시도한 적이 없다 …)
이를 통해 맵 조회 또는 업데이트를 수행 할 때마다 새 객체를 생성하는 오버 헤드를 피할 수 있습니다. 그리고 일반적인 Map
작업은 일반처럼 O (1) …이어야합니다 HashMap
.
그리고 그들이 선택한 구현 선택을 받아 들일 준비가 되었다면 Apache Commons CaseInsensitiveMap
는 사용자 정의 / 전문화 작업 AbstractHashedMap
을 수행합니다.
그러나 O (logN) get
및 put
연산이 허용되는 TreeMap
경우 대소 문자를 구분하지 않는 문자열 비교기가 옵션입니다. 예를 들어 String.CASE_INSENSITIVE_ORDER
.
새 임시 String 객체에게 당신이 할 때마다 생성 괜찮다면 그리고 put
나 get
, 다음 이씨의 대답은 잘합니다. (하지만, 그렇게 한 경우 원래 키 케이스를 유지하지 않을 것입니다 …)
답변
서브 클래스는 HashMap
그 낮은 경우상의 키 버전을 생성 put
하고 get
(그리고 아마도 다른 키 지향 방법).
또는 a HashMap
를 새 클래스에 합성하고 모든 것을지도에 위임하지만 키를 번역하십시오.
원래 키를 유지해야하는 경우 이중 맵을 유지하거나 원래 키를 값과 함께 저장할 수 있습니다.
답변
두 가지 선택이 떠 오릅니다.
s.toUpperCase().hashCode();
의 키로 직접을 사용할 수 있습니다Map
.- 대소 문자를 무시
TreeMap<String>
하는 사용자 정의와 함께를 사용할 수 있습니다Comparator
.
그렇지 않으면 새로운 종류의 문자열을 정의하는 대신 솔루션을 선호하는 경우 필요한 대소 문자를 구분하지 않는 기능으로 새 맵을 구현합니다.
답변
해시 코드를 기억하기 위해 문자열을 “랩핑”하는 것이 좋지 않을 것입니다. 일반적인 String 클래스에서 hashCode ()는 처음으로 O (N)이고 나중에 사용하기 위해 유지되므로 O (1)입니다.
public class HashWrap {
private final String value;
private final int hash;
public String get() {
return value;
}
public HashWrap(String value) {
this.value = value;
String lc = value.toLowerCase();
this.hash = lc.hashCode();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o instanceof HashWrap) {
HashWrap that = (HashWrap) o;
return value.equalsIgnoreCase(that.value);
} else {
return false;
}
}
@Override
public int hashCode() {
return this.hash;
}
//might want to implement compare too if you want to use with SortedMaps/Sets.
}
이를 통해 Java에서 Hashtable의 모든 구현을 사용하고 O (1) hasCode ()를 가질 수 있습니다.
답변
Eclipse Collections 에서 HashingStrategy 를 사용할 수 있습니다Map
HashingStrategy<String> hashingStrategy =
HashingStrategies.fromFunction(String::toUpperCase);
MutableMap<String, String> node = HashingStrategyMaps.mutable.of(hashingStrategy);
참고 : 저는 Eclipse Collections에 기고자입니다.