Sequência sem distinção entre maiúsculas e minúsculas como chave HashMap

178

Gostaria de usar uma seqüência sem distinção entre maiúsculas e minúsculas como uma chave HashMap pelos seguintes motivos.

  • Durante a inicialização, meu programa cria HashMap com String definida pelo usuário
  • Durante o processamento de um evento (tráfego de rede no meu caso), posso receber String em um caso diferente, mas conseguir localizar o <key, value>HashMap ignorando o caso que recebi do tráfego.

Eu segui essa abordagem

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

Por isso, estou criando um novo objeto de CaseInsensitiveString para cada evento. Portanto, pode afetar o desempenho.

Existe alguma outra maneira de resolver esse problema?

rs
fonte
3
[Existe uma boa maneira de ter um Map <String,?> Chegar e colocar ignorar caso?] [1] [1]: stackoverflow.com/questions/212562/...
Beau Grantham
Comentei os problemas abaixo, mas eles estão abaixo do limite para que as pessoas não os vejam. Cuidado com a subclasse de HashMap. O JDK8 mudou a implementação e agora você precisa substituir o putAll (pelo menos) para que essas sugestões funcionem.
Steve N
Isso deve funcionar bem. Você pode usar um peso de mosca para se livrar da nova instanciação de objeto.
Topkara 9/08/15

Respostas:

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

Isso é realmente tudo o que você precisa.

Roel Spilker
fonte
6
Esse é o mais simples, de longe, e também preserva o caso das chaves ao iterá-las.
Ralf
Isso é lindo! Esta foi a peça final do quebra-cabeça para criar uma estrutura ordenada no ColdFusion que preserva a capacidade de usar a notação de pontos. Em vez de var struct = {} ou var struct = structnew (), você pode usar var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); COMPLETAMENTE, mas funciona;)
Eric Fuller
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
pllee
5
Não há necessidade de, <K extends String>pois Stringé final: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker 03/02
19
Lembre-se de que o TreeMap não é um tempo constante para operações básicas. Não é um problema para a maioria dos aplicativos, mas vale a pena lembrar. No JavaDoc: "Esta implementação fornece um custo garantido de log (n) para as operações containsKey, get, put e remove. Os algoritmos são adaptações daqueles em Introdução aos Algoritmos de Cormen, Leiserson e Rivest."
James Schek
57

Como sugerido por Guido García em sua resposta aqui :

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

Ou

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

Vishal
fonte
28
Que tal contém, putAll, etc.?
assylias 25/09/12
14
Isso não funciona em alguns idiomas, como o turco. Google "The turkey test"
Hugo
5
@assylias: true, containsKey()e remove()deve ser substituído da mesma maneira que get(). a HashMap.putAll()implementação usa put(), portanto isso não deve ser um problema - desde que a implementação do HashMap permaneça a mesma. ;) também a get()assinatura do método recebe um Objectargumento as, não a String. O código também não testa para uma chave nula:super.get(key == null ? null : key.toString().toLowercase());
sfera
observe que se você precisar do construtor de cópia HashMap(<? extends String, ? extends String> anotherMap), não deverá chamar a super implementação do mesmo construtor, pois essa operação não garantirá que suas chaves sejam minúsculas. você poderia usar: em super(anotherMap.size()); putAll(anotherMap);vez disso.
S2
E se você quiser que os valores do mapa não sejam cadeias de caracteres? (ie CaseInsensitiveMap<String, Integer>)
Adam Parkin
16

Uma abordagem é criar uma subclasse personalizada da classe Apache Commons AbstractHashedMap, substituindo os métodos hashe isEqualKeyspara executar hash sem distinção entre maiúsculas e minúsculas e comparação de chaves. (Nota - eu nunca tentei isso sozinho ...)

Isso evita a sobrecarga de criação de novos objetos toda vez que você precisa fazer uma pesquisa ou atualização no mapa. E as Mapoperações comuns devem O (1) ... como um regular HashMap.

E se você estiver preparado para aceitar as escolhas de implementação que eles fizeram, o Apache Commons CaseInsensitiveMapfará o trabalho de customização / especialização AbstractHashedMappara você.


Mas se O (logN) gete putoperações forem aceitáveis, a TreeMapcom um comparador de cadeias sem distinção entre maiúsculas e minúsculas é uma opção; por exemplo, usando String.CASE_INSENSITIVE_ORDER.

E se você não se importa em criar um novo objeto String temporário toda vez que faz um putou get, então a resposta de Vishal é ótima. (Porém, note que você não preservaria o caso original das chaves se fizesse isso ...)

Stephen C
fonte
6

Subclasse HashMape crie uma versão que minúscula a chave pute get(e provavelmente os outros métodos orientados a chave).

Ou compor a HashMapna nova classe e delegar tudo ao mapa, mas traduza as chaves.

Se você precisar manter a chave original, poderá manter mapas duplos ou armazenar a chave original junto com o valor.

Dave Newton
fonte
Você quer dizer String.toLowerCase () durante a pesquisa?
rs
@ user710178 Não apenas durante a pesquisa, mas também durante o armazenamento.
Dave Newton
@ user710178 Ah, claro, como essa outra resposta indica, isso já existe, se você não se importa com uma dependência adicional.
Dave Newton
@StephenC Se atender às suas necessidades, com certeza; O OP especificou a HashMap, então foi isso que eu escolhi :) Ah, você quer dizer o do Commons; Entendo. Eu acho que, contanto que você não precisa dele generified (ou eles finalmente têm genéricos agora?)
Dave Newton
1
Para o JDK 8 e superior, você também precisará (pelo menos) substituir o putAll conforme a implementação foi alterada.
Steve N
4

Duas opções me vêm à mente:

  1. Você pode usar diretamente o s.toUpperCase().hashCode();como a chave do Map.
  2. Você pode usar um TreeMap<String>com um costume Comparatorque ignore o caso.

Caso contrário, se você preferir sua solução, em vez de definir um novo tipo de String, eu prefiro implementar um novo Mapa com a funcionalidade de insensibilidade de caso necessária.

Gabriel Belingueres
fonte
3

Não seria melhor "quebrar" a String para memorizar o hashCode. Na classe String normal, hashCode () é O (N) pela primeira vez e depois é O (1), pois é mantido para uso futuro.

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.
}

Isso permitiria usar qualquer implementação do Hashtable em java e ter O (1) hasCode ().

le-doude
fonte
3

Você pode usar um HashingStrategy baseado Mapem Eclipse Collections

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

Nota: Sou colaborador do Eclipse Collections.

Nikhil Nanivadekar
fonte
2

Com base em outras respostas, existem basicamente duas abordagens: subclassificação HashMapou empacotamento String. O primeiro requer um pouco mais de trabalho. De fato, se você deseja fazer isso corretamente, você deve substituir quase todos os métodos ( containsKey, entrySet, get, put, putAll and remove).

Enfim, tem um problema. Se você deseja evitar problemas futuros, deve especificar uma operação Localeno Stringcaso. Então você criaria novos métodos ( get(String, Locale), ...). Tudo é mais fácil e mais claro.

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
}

E bem, sobre suas preocupações com o desempenho: a otimização prematura é a raiz de todos os males :)

sinuhepop
fonte
2
"E bem, sobre suas preocupações com o desempenho: a otimização prematura é a raiz de todos os males :)" - Pelo contrário, usar isso como uma desculpa para sempre escrever código ineficiente é o que é mau.
Gordon
1
Na verdade, @Gordon, ambos são igualmente ruins, dependendo do contexto. O rótulo "mal" é um sinal de pensamento em preto e branco, como "melhores práticas" e várias outras frases inúteis que muitas pessoas de TI costumam usar. Melhor evitá-lo completamente.
Stephen C
Descobri que dizer às pessoas que elas não estão seguindo as "melhores práticas" tende a produzir menos coisas do que dizer que elas têm más práticas.
Gordon
0

Este é um adaptador para HashMaps que eu implementei para um projeto recente. Funciona de maneira semelhante ao que o @SandyR faz, mas encapsula a lógica de conversão para que você não converta manualmente as strings em um objeto wrapper.

Eu usei os recursos do Java 8, mas com algumas alterações, você pode adaptá-lo às versões anteriores. Testei-o para os cenários mais comuns, exceto as novas funções de fluxo do Java 8.

Basicamente, ele envolve um HashMap, direciona todas as funções para ele enquanto converte seqüências de caracteres para / de um objeto wrapper. Mas eu também tive que adaptar o KeySet e o EntrySet porque eles encaminham algumas funções para o próprio mapa. Então, retorno dois novos conjuntos de chaves e entradas que realmente envolvem o keySet original () e o entrySet ().

Uma observação: o Java 8 mudou a implementação do método putAll, que não consegui encontrar uma maneira fácil de substituir. Portanto, a implementação atual pode ter um desempenho degradado, especialmente se você usar putAll () para um grande conjunto de dados.

Informe-me se você encontrar um bug ou tiver sugestões para melhorar o código.

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


}
Cagatay Kalan
fonte
0

Por isso, estou criando um novo objeto de CaseInsensitiveString para cada evento. Portanto, pode afetar o desempenho.

Criar wrappers ou converter chave em minúscula antes da pesquisa cria novos objetos. Escrever sua própria implementação java.util.Map é a única maneira de evitar isso. Não é muito difícil, e a IMO vale a pena. Eu encontrei a seguinte função hash para funcionar muito bem, até algumas centenas de chaves.

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

Que tal usar java 8 streams.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Amarendra Reddy
fonte
Isso não permite que você pesquise valores no mapa de maneira que não diferencia maiúsculas de minúsculas.
Gili
equalsignorecase faria isso não é?
Amarendra Reddy
Você está construindo uma lista. O OP pediu um mapa.
Gili
Isso destrói o benefício de complexidade O (1) de um mapa.
Paul Rooney