HashMap 키와 같이 대소 문자를 구분하지 않는 문자열


178

다음과 같은 이유로 대소 문자를 구분하지 않는 문자열을 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의 새 객체를 만들고 있습니다. 따라서 성능이 저하 될 수 있습니다.

이 문제를 해결하는 다른 방법이 있습니까?


3
[지도 가질 수있는 좋은 방법이 있나요 <문자열,?> 얻을 경우를 무시 넣어?] [1] [1] : stackoverflow.com/questions/212562/...
보 랜덤

아래 문제에 대해 언급했지만, 사람들이 문제를 볼 수 없도록 임계 값 아래에 있습니다. HashMap을 서브 클래 싱하는 것을주의하십시오. JDK8은 구현을 변경했으며 이제 제안 사항이 작동하도록 putAll을 재정의해야합니다.
Steve N

이것은 잘 작동합니다. 플라이급을 사용하여 새 객체 인스턴스화를 제거 할 수 있습니다.
topkara

답변:


331
Map<String, String> nodeMap = 
    new TreeMap<>(String.CASE_INSENSITIVE_ORDER);

그게 당신이 필요한 전부입니다.


6
이것은 지금까지 가장 간단하며 반복 할 때 키의 경우를 유지합니다.
Ralf

이것은 아름답다! ColdFusion에서 점 표기법을 사용할 수있는 정렬 된 구조체를 만들기위한 퍼즐의 마지막 조각입니다. var struct = {} 또는 var struct = structnew () 대신 var struct = createObject ( 'java', 'java.util.TreeMap'). init (createObject ( 'java', 'java.lang.String')를 사용할 수 있습니다. ) .CASE_INSENSITIVE_ORDER); FUGLY, 그러나 그것은 작동합니다;)
Eric Fuller

public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
pllee

5
에 대한 필요가 <K extends String>보낸 String마지막입니다 : public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
로엘 Spilker

19
TreeMap은 기본 작업을위한 일정한 시간이 아닙니다. 대부분의 응용 프로그램에는 문제가되지 않지만 명심해야합니다. JavaDoc에서 : "이 구현은 containsKey, get, put 및 remove 작업에 대해 보장 된 log (n) 시간 비용을 제공합니다. 알고리즘은 Cormen, Leiserson 및 Rivest의 Algoithms 소개에 적용됩니다."
James Schek

57

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());
    }
}

또는

https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html


28
포함, putAll 등은 어떻습니까?
assylias

14
터키어와 같은 일부 언어에서는 작동하지 않습니다. 구글 "터키 테스트"
휴고

5
@assylias : true이며 containsKey()remove()같은 방식으로 재정의해야합니다 get(). HashMap.putAll()구현의 사용은 put(), 그래서 문제가되지 않습니다 -만큼 HashMap의 구현 숙박 동일있다. ;) 또한 get()메소드 서명은 a가 아닌 Objectas 인수를 사용합니다 String. 이 코드는 또한 null 키를 테스트하지 않습니다.super.get(key == null ? null : key.toString().toLowercase());
sfera

copy-constructor가 필요한 경우 HashMap(<? extends String, ? extends String> anotherMap), 해당 조작과 동일한 생성자의 수퍼 구현을 호출해서는 안되며 키가 소문자임을 보장하지 않습니다. super(anotherMap.size()); putAll(anotherMap);대신 사용할 수 있습니다 .
Sfera

지도의 값이 문자열이 아닌 경우 어떻게합니까? (예 CaseInsensitiveMap<String, Integer>)
아담 파킨

16

한 가지 방법은 Apache Commons AbstractHashedMap클래스 의 사용자 정의 서브 클래스를 작성하여 대소 문자를 구분하지 않는 해시 및 키 비교를 수행하기 위해 hashisEqualKeys메소드를 대체하는 것입니다. (참고-나는 이것을 직접 시도한 적이 없다 ...)

이를 통해 맵 조회 또는 업데이트를 수행 할 때마다 새 객체를 생성하는 오버 헤드를 피할 수 있습니다. 그리고 일반적인 Map작업은 일반처럼 O (1) ...이어야합니다 HashMap.

그리고 그들이 선택한 구현 선택을 받아 들일 준비가 되었다면 Apache Commons CaseInsensitiveMap는 사용자 정의 / 전문화 작업 AbstractHashedMap을 수행합니다.


그러나 O (logN) getput연산이 허용되는 TreeMap경우 대소 문자를 구분하지 않는 문자열 비교기가 옵션입니다. 예를 들어 String.CASE_INSENSITIVE_ORDER.

새 임시 String 객체에게 당신이 할 때마다 생성 괜찮다면 그리고 putget, 다음 이씨의 대답은 잘합니다. (하지만, 그렇게 한 경우 원래 키 케이스를 유지하지 않을 것입니다 ...)


6

서브 클래스는 HashMap그 낮은 경우상의 키 버전을 생성 put하고 get(그리고 아마도 다른 키 지향 방법).

또는 a HashMap를 새 클래스에 합성하고 모든 것을지도에 위임하지만 키를 번역하십시오.

원래 키를 유지해야하는 경우 이중 맵을 유지하거나 원래 키를 값과 함께 저장할 수 있습니다.


조회하는 동안 String.toLowerCase ()합니까?
rs

@하지 user710178 단지 조회 동안 만 보관시뿐만 아니라.
Dave Newton

@ user710178 아, 다른 대답에서 지적했듯이 추가 종속성이 마음에 들지 않으면 이미 존재합니다.
Dave Newton

@StephenC 그것이 당신의 요구를 충족한다면, 확실히; OP는을 지정 HashMap했으므로 다음과 같이했습니다. :) 당신은 Commons 하나를 의미합니다. 내가 참조. 당신이 그것을 생성 할 필요가없는 한 (또는 마침내 제네릭을 가지고 있습니까?)
Dave Newton

1
JDK 8 이상에서는 구현이 변경됨에 따라 putAll을 (적어도) 재정의해야합니다.
Steve N

4

두 가지 선택이 떠 오릅니다.

  1. s.toUpperCase().hashCode();의 키로 직접을 사용할 수 있습니다 Map.
  2. 대소 문자를 무시 TreeMap<String>하는 사용자 정의와 함께를 사용할 수 있습니다 Comparator.

그렇지 않으면 새로운 종류의 문자열을 정의하는 대신 솔루션을 선호하는 경우 필요한 대소 문자를 구분하지 않는 기능으로 새 맵을 구현합니다.


3

해시 코드를 기억하기 위해 문자열을 "랩핑"하는 것이 좋지 않을 것입니다. 일반적인 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 ()를 가질 수 있습니다.


3

Eclipse Collections 에서 HashingStrategy 를 사용할 수 있습니다Map

HashingStrategy<String> hashingStrategy =
    HashingStrategies.fromFunction(String::toUpperCase);
MutableMap<String, String> node = HashingStrategyMaps.mutable.of(hashingStrategy);

참고 : 저는 Eclipse Collections에 기고자입니다.


2

다른 답변을 바탕으로 기본적으로 두 가지 접근 방식이 있습니다 : 서브 클래스 링 HashMap또는 줄 바꿈 String. 첫 번째는 조금 더 많은 작업이 필요합니다. 실제로 올바르게 수행하려면 거의 모든 메소드 ( containsKey, entrySet, get, put, putAll and remove)를 대체해야합니다 .

어쨌든 문제가 있습니다. 당신은 미래의 문제를 방지하려는 경우를 지정해야합니다 LocaleString경우 작업. 따라서 새로운 메소드 ( get(String, Locale), ...)를 만들 것 입니다. 모든 것이 쉽고 명확하게 줄 바꿈됩니다.

public final class CaseInsensitiveString {

    private final String s;

    public CaseInsensitiveString(String s, Locale locale) {
        this.s = s.toUpperCase(locale);
    }

    // equals, hashCode & toString, no need for memoizing hashCode
}

그리고 성능에 대한 걱정에 대해 : 조기 최적화는 모든 악의 근원입니다 :)


2
"그리고 성능에 대한 당신의 걱정에 대해 : 조기 최적화는 모든 악의 근원입니다 :)"-반대로, 비효율적 인 코드를 작성하는 변명으로 그것을 사용하는 것은 악한 것입니다.
Gordon

1
실제로 @ Gordon은 상황에 따라 똑같이 나쁩니다. "악"이라는 레이블은 "모범 사례"및 많은 IT 사람들이 사용하는 다양한 도움이되지 않는 문구와 같은 흑백 사고의 표시입니다. 완전히 피하는 것이 가장 좋습니다.
Stephen C

나는 사람들이 "모범 사례"를 따르지 않는다고 말하는 것은 그들이 나쁜 습관을 가지고 있다고 말하는 것보다 발 뒤꿈치를 덜 파는 경향이 있다는 것을 발견했습니다.
고든

0

이것은 최근 프로젝트를 위해 구현 한 HashMaps 용 어댑터입니다. @SandyR과 비슷한 방식으로 작동하지만 변환 논리를 캡슐화하여 문자열을 래퍼 객체로 수동으로 변환하지 않습니다.

Java 8 기능을 사용했지만 약간의 변경만으로 이전 버전에 맞게 조정할 수 있습니다. 새로운 Java 8 스트림 기능을 제외하고 가장 일반적인 시나리오에서 테스트했습니다.

기본적으로 HashMap을 래핑하고 문자열을 래퍼 객체와 변환하는 동안 모든 함수를 전달합니다. 그러나 KeySet 및 EntrySet도 일부 기능을 맵 자체에 전달하기 때문에 적응해야했습니다. 따라서 원래 keySet () 및 entrySet ()을 실제로 래핑하는 키와 항목에 대해 두 개의 새로운 세트를 반환합니다.

참고 사항 : Java 8은 putAll 메소드의 구현을 변경하여 쉽게 재정의하는 방법을 찾을 수 없었습니다. 따라서 대규모 데이터 세트에 putAll ()을 사용하는 경우 현재 구현에서 성능이 저하 될 수 있습니다.

버그를 발견하거나 코드 개선을위한 제안 사항이 있으면 알려주십시오.

패키지 webbit.collections;

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;


public class CaseInsensitiveMapAdapter<T> implements Map<String,T>
{
    private Map<CaseInsensitiveMapKey,T> map;
    private KeySet keySet;
    private EntrySet entrySet;


    public CaseInsensitiveMapAdapter()
    {
    }

    public CaseInsensitiveMapAdapter(Map<String, T> map)
    {
        this.map = getMapImplementation();
        this.putAll(map);
    }

    @Override
    public int size()
    {
        return getMap().size();
    }

    @Override
    public boolean isEmpty()
    {
        return getMap().isEmpty();
    }

    @Override
    public boolean containsKey(Object key)
    {
        return getMap().containsKey(lookupKey(key));
    }

    @Override
    public boolean containsValue(Object value)
    {
        return getMap().containsValue(value);
    }

    @Override
    public T get(Object key)
    {
        return getMap().get(lookupKey(key));
    }

    @Override
    public T put(String key, T value)
    {
        return getMap().put(lookupKey(key), value);
    }

    @Override
    public T remove(Object key)
    {
        return getMap().remove(lookupKey(key));
    }

    /***
     * I completely ignore Java 8 implementation and put one by one.This will be slower.
     */
    @Override
    public void putAll(Map<? extends String, ? extends T> m)
    {
        for (String key : m.keySet()) {
            getMap().put(lookupKey(key),m.get(key));
        }
    }

    @Override
    public void clear()
    {
        getMap().clear();
    }

    @Override
    public Set<String> keySet()
    {
        if (keySet == null)
            keySet = new KeySet(getMap().keySet());
        return keySet;
    }

    @Override
    public Collection<T> values()
    {
        return getMap().values();
    }

    @Override
    public Set<Entry<String, T>> entrySet()
    {
        if (entrySet == null)
            entrySet = new EntrySet(getMap().entrySet());
        return entrySet;
    }

    @Override
    public boolean equals(Object o)
    {
        return getMap().equals(o);
    }

    @Override
    public int hashCode()
    {
        return getMap().hashCode();
    }

    @Override
    public T getOrDefault(Object key, T defaultValue)
    {
        return getMap().getOrDefault(lookupKey(key), defaultValue);
    }

    @Override
    public void forEach(final BiConsumer<? super String, ? super T> action)
    {
        getMap().forEach(new BiConsumer<CaseInsensitiveMapKey, T>()
        {
            @Override
            public void accept(CaseInsensitiveMapKey lookupKey, T t)
            {
                action.accept(lookupKey.key,t);
            }
        });
    }

    @Override
    public void replaceAll(final BiFunction<? super String, ? super T, ? extends T> function)
    {
        getMap().replaceAll(new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return function.apply(lookupKey.key,t);
            }
        });
    }

    @Override
    public T putIfAbsent(String key, T value)
    {
        return getMap().putIfAbsent(lookupKey(key), value);
    }

    @Override
    public boolean remove(Object key, Object value)
    {
        return getMap().remove(lookupKey(key), value);
    }

    @Override
    public boolean replace(String key, T oldValue, T newValue)
    {
        return getMap().replace(lookupKey(key), oldValue, newValue);
    }

    @Override
    public T replace(String key, T value)
    {
        return getMap().replace(lookupKey(key), value);
    }

    @Override
    public T computeIfAbsent(String key, final Function<? super String, ? extends T> mappingFunction)
    {
        return getMap().computeIfAbsent(lookupKey(key), new Function<CaseInsensitiveMapKey, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey)
            {
                return mappingFunction.apply(lookupKey.key);
            }
        });
    }

    @Override
    public T computeIfPresent(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
    {
        return getMap().computeIfPresent(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return remappingFunction.apply(lookupKey.key, t);
            }
        });
    }

    @Override
    public T compute(String key, final BiFunction<? super String, ? super T, ? extends T> remappingFunction)
    {
        return getMap().compute(lookupKey(key), new BiFunction<CaseInsensitiveMapKey, T, T>()
        {
            @Override
            public T apply(CaseInsensitiveMapKey lookupKey, T t)
            {
                return remappingFunction.apply(lookupKey.key,t);
            }
        });
    }

    @Override
    public T merge(String key, T value, BiFunction<? super T, ? super T, ? extends T> remappingFunction)
    {
        return getMap().merge(lookupKey(key), value, remappingFunction);
    }

    protected  Map<CaseInsensitiveMapKey,T> getMapImplementation() {
        return new HashMap<>();
    }

    private Map<CaseInsensitiveMapKey,T> getMap() {
        if (map == null)
            map = getMapImplementation();
        return map;
    }

    private CaseInsensitiveMapKey lookupKey(Object key)
    {
        return new CaseInsensitiveMapKey((String)key);
    }

    public class CaseInsensitiveMapKey {
        private String key;
        private String lookupKey;

        public CaseInsensitiveMapKey(String key)
        {
            this.key = key;
            this.lookupKey = key.toUpperCase();
        }

        @Override
        public boolean equals(Object o)
        {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            CaseInsensitiveMapKey that = (CaseInsensitiveMapKey) o;

            return lookupKey.equals(that.lookupKey);

        }

        @Override
        public int hashCode()
        {
            return lookupKey.hashCode();
        }
    }

    private class KeySet implements Set<String> {

        private Set<CaseInsensitiveMapKey> wrapped;

        public KeySet(Set<CaseInsensitiveMapKey> wrapped)
        {
            this.wrapped = wrapped;
        }


        private List<String> keyList() {
            return stream().collect(Collectors.toList());
        }

        private Collection<CaseInsensitiveMapKey> mapCollection(Collection<?> c) {
            return c.stream().map(it -> lookupKey(it)).collect(Collectors.toList());
        }

        @Override
        public int size()
        {
            return wrapped.size();
        }

        @Override
        public boolean isEmpty()
        {
            return wrapped.isEmpty();
        }

        @Override
        public boolean contains(Object o)
        {
            return wrapped.contains(lookupKey(o));
        }

        @Override
        public Iterator<String> iterator()
        {
            return keyList().iterator();
        }

        @Override
        public Object[] toArray()
        {
            return keyList().toArray();
        }

        @Override
        public <T> T[] toArray(T[] a)
        {
            return keyList().toArray(a);
        }

        @Override
        public boolean add(String s)
        {
            return wrapped.add(lookupKey(s));
        }

        @Override
        public boolean remove(Object o)
        {
            return wrapped.remove(lookupKey(o));
        }

        @Override
        public boolean containsAll(Collection<?> c)
        {
            return keyList().containsAll(c);
        }

        @Override
        public boolean addAll(Collection<? extends String> c)
        {
            return wrapped.addAll(mapCollection(c));
        }

        @Override
        public boolean retainAll(Collection<?> c)
        {
            return wrapped.retainAll(mapCollection(c));
        }

        @Override
        public boolean removeAll(Collection<?> c)
        {
            return wrapped.removeAll(mapCollection(c));
        }

        @Override
        public void clear()
        {
            wrapped.clear();
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(lookupKey(o));
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }

        @Override
        public Spliterator<String> spliterator()
        {
            return keyList().spliterator();
        }

        @Override
        public boolean removeIf(Predicate<? super String> filter)
        {
            return wrapped.removeIf(new Predicate<CaseInsensitiveMapKey>()
            {
                @Override
                public boolean test(CaseInsensitiveMapKey lookupKey)
                {
                    return filter.test(lookupKey.key);
                }
            });
        }

        @Override
        public Stream<String> stream()
        {
            return wrapped.stream().map(it -> it.key);
        }

        @Override
        public Stream<String> parallelStream()
        {
            return wrapped.stream().map(it -> it.key).parallel();
        }

        @Override
        public void forEach(Consumer<? super String> action)
        {
            wrapped.forEach(new Consumer<CaseInsensitiveMapKey>()
            {
                @Override
                public void accept(CaseInsensitiveMapKey lookupKey)
                {
                    action.accept(lookupKey.key);
                }
            });
        }
    }

    private class EntrySet implements Set<Map.Entry<String,T>> {

        private Set<Entry<CaseInsensitiveMapKey,T>> wrapped;

        public EntrySet(Set<Entry<CaseInsensitiveMapKey,T>> wrapped)
        {
            this.wrapped = wrapped;
        }


        private List<Map.Entry<String,T>> keyList() {
            return stream().collect(Collectors.toList());
        }

        private Collection<Entry<CaseInsensitiveMapKey,T>> mapCollection(Collection<?> c) {
            return c.stream().map(it -> new CaseInsensitiveEntryAdapter((Entry<String,T>)it)).collect(Collectors.toList());
        }

        @Override
        public int size()
        {
            return wrapped.size();
        }

        @Override
        public boolean isEmpty()
        {
            return wrapped.isEmpty();
        }

        @Override
        public boolean contains(Object o)
        {
            return wrapped.contains(lookupKey(o));
        }

        @Override
        public Iterator<Map.Entry<String,T>> iterator()
        {
            return keyList().iterator();
        }

        @Override
        public Object[] toArray()
        {
            return keyList().toArray();
        }

        @Override
        public <T> T[] toArray(T[] a)
        {
            return keyList().toArray(a);
        }

        @Override
        public boolean add(Entry<String,T> s)
        {
            return wrapped.add(null );
        }

        @Override
        public boolean remove(Object o)
        {
            return wrapped.remove(lookupKey(o));
        }

        @Override
        public boolean containsAll(Collection<?> c)
        {
            return keyList().containsAll(c);
        }

        @Override
        public boolean addAll(Collection<? extends Entry<String,T>> c)
        {
            return wrapped.addAll(mapCollection(c));
        }

        @Override
        public boolean retainAll(Collection<?> c)
        {
            return wrapped.retainAll(mapCollection(c));
        }

        @Override
        public boolean removeAll(Collection<?> c)
        {
            return wrapped.removeAll(mapCollection(c));
        }

        @Override
        public void clear()
        {
            wrapped.clear();
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(lookupKey(o));
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }

        @Override
        public Spliterator<Entry<String,T>> spliterator()
        {
            return keyList().spliterator();
        }

        @Override
        public boolean removeIf(Predicate<? super Entry<String, T>> filter)
        {
            return wrapped.removeIf(new Predicate<Entry<CaseInsensitiveMapKey, T>>()
            {
                @Override
                public boolean test(Entry<CaseInsensitiveMapKey, T> entry)
                {
                    return filter.test(new FromCaseInsensitiveEntryAdapter(entry));
                }
            });
        }

        @Override
        public Stream<Entry<String,T>> stream()
        {
            return wrapped.stream().map(it -> new Entry<String, T>()
            {
                @Override
                public String getKey()
                {
                    return it.getKey().key;
                }

                @Override
                public T getValue()
                {
                    return it.getValue();
                }

                @Override
                public T setValue(T value)
                {
                    return it.setValue(value);
                }
            });
        }

        @Override
        public Stream<Map.Entry<String,T>> parallelStream()
        {
            return StreamSupport.stream(spliterator(), true);
        }

        @Override
        public void forEach(Consumer<? super Entry<String, T>> action)
        {
            wrapped.forEach(new Consumer<Entry<CaseInsensitiveMapKey, T>>()
            {
                @Override
                public void accept(Entry<CaseInsensitiveMapKey, T> entry)
                {
                    action.accept(new FromCaseInsensitiveEntryAdapter(entry));
                }
            });
        }
    }

    private class EntryAdapter implements Map.Entry<String,T> {
        private Entry<String,T> wrapped;

        public EntryAdapter(Entry<String, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public String getKey()
        {
            return wrapped.getKey();
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }

        @Override
        public boolean equals(Object o)
        {
            return wrapped.equals(o);
        }

        @Override
        public int hashCode()
        {
            return wrapped.hashCode();
        }


    }

    private class CaseInsensitiveEntryAdapter implements Map.Entry<CaseInsensitiveMapKey,T> {

        private Entry<String,T> wrapped;

        public CaseInsensitiveEntryAdapter(Entry<String, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public CaseInsensitiveMapKey getKey()
        {
            return lookupKey(wrapped.getKey());
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }
    }

    private class FromCaseInsensitiveEntryAdapter implements Map.Entry<String,T> {

        private Entry<CaseInsensitiveMapKey,T> wrapped;

        public FromCaseInsensitiveEntryAdapter(Entry<CaseInsensitiveMapKey, T> wrapped)
        {
            this.wrapped = wrapped;
        }

        @Override
        public String getKey()
        {
            return wrapped.getKey().key;
        }

        @Override
        public T getValue()
        {
            return wrapped.getValue();
        }

        @Override
        public T setValue(T value)
        {
            return wrapped.setValue(value);
        }
    }


}

0

이 때문에 모든 이벤트에 대해 CaseInsensitiveString의 새 객체를 만들고 있습니다. 따라서 성능이 저하 될 수 있습니다.

조회하기 전에 랩퍼를 작성하거나 키를 소문자로 변환하면 둘 다 새 오브젝트를 작성합니다. 자신의 java.util.Map 구현을 작성하는 것이 이것을 피할 수있는 유일한 방법입니다. 너무 어렵지 않고 IMO는 그만한 가치가 있습니다. 다음 해시 함수가 최대 수백 개의 키까지 잘 작동한다는 것을 알았습니다.

static int ciHashCode(String string)
{
    // length and the low 5 bits of hashCode() are case insensitive
    return (string.hashCode() & 0x1f)*33 + string.length();
}

-3

Java 8 스트림을 사용하는 것은 어떻습니까.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())

이렇게하면 대소 문자를 구분하지 않고 맵에서 값을 찾을 수 없습니다.
길리

equalsignorecase는 그렇지 않습니까?
Amarendra Reddy

목록을 작성 중입니다. OP가지도를 요청했습니다.
길리

이것은 맵의 O (1) 복잡성 이점을 파괴합니다.
Paul Rooney
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.