Kasus string tidak sensitif sebagai kunci HashMap

178

Saya ingin menggunakan string peka huruf besar-kecil sebagai kunci HashMap karena alasan berikut.

  • Selama inisialisasi, program saya membuat HashMap dengan String yang ditentukan pengguna
  • Saat memproses suatu peristiwa (lalu lintas jaringan dalam kasus saya), saya mungkin menerima String dalam kasus yang berbeda tetapi saya harus dapat menemukan <key, value> dari HashMap mengabaikan kasus yang saya terima dari lalu lintas.

Saya telah mengikuti pendekatan ini

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

Karena itu, saya membuat objek baru CaseInsensitiveString untuk setiap acara. Jadi, itu mungkin mengenai kinerja.

Apakah ada cara lain untuk menyelesaikan masalah ini?

rs
sumber
3
[Apakah ada cara yang baik untuk memiliki Map <String,?> Dapatkan dan letakkan kotak abaikan?] [1] [1]: stackoverflow.com/questions/212562/…
Beau Grantham
Saya telah mengomentari masalah di bawah ini, tetapi mereka berada di bawah ambang batas sehingga orang mungkin tidak melihatnya. Waspadalah terhadap subkelas HashMap. JDK8 telah mengubah implementasinya dan Anda sekarang harus mengesampingkan putAll (setidaknya) untuk mendapatkan saran agar berfungsi.
Steve N
Ini seharusnya bekerja dengan baik. Anda dapat menggunakan kelas terbang untuk menyingkirkan instantiasi objek baru.
topkara

Jawaban:

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

Itu benar-benar yang Anda butuhkan.

Roel Spilker
sumber
6
Ini adalah yang paling sederhana sejauh ini, dan juga menjaga case kunci ketika iterasi melalui mereka.
Ralf
Ini indah! Ini adalah bagian terakhir dari teka-teki untuk membuat struct yang dipesan di ColdFusion yang mempertahankan kemampuan untuk menggunakan notasi titik. Alih-alih var struct = {} atau var struct = structnew () Anda dapat menggunakan var struct = createObject ('java', 'java.util.TreeMap'). Init (createObject ('java', 'java.lang.String' ) .CASE_INSENSITIVE_ORDER); FUGLY, tapi berhasil;)
Eric Fuller
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
plve
5
Tidak perlu <K extends String>karena Stringsudah final: public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Roel Spilker
19
Perlu diingat bahwa TreeMap bukan waktu yang konstan untuk operasi dasar. Bukan masalah untuk sebagian besar aplikasi, tetapi perlu diingat. Dari JavaDoc: "Implementasi ini memberikan jaminan log (n) biaya waktu untuk containKey, dapatkan, pasang dan hapus operasi. Algoritma adalah adaptasi dari yang ada di Cormen, Leiserson, dan Rivest's Introduction to Algorithms."
James Schek
57

Seperti yang disarankan oleh Guido García dalam jawaban mereka di sini :

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

Atau

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

Vishal
sumber
28
Bagaimana dengan berisi, putAll, dll?
assylias
14
Ini tidak berfungsi dalam beberapa bahasa, seperti Turki. Google "Tes kalkun"
Hugo
5
@assylias: benar, containsKey()dan remove()harus diganti dengan cara yang sama get(). yang HashMap.putAll()menggunakan implementasi put(), sehingga seharusnya tidak menjadi masalah - selama HashMap pelaksanaan tetap sama. ;) juga get()metode tanda tangan mengambil Objectargumen, bukan a String. kode juga tidak menguji kunci null:super.get(key == null ? null : key.toString().toLowercase());
sfera
perhatikan bahwa jika Anda memerlukan copy-constructor HashMap(<? extends String, ? extends String> anotherMap), maka Anda tidak boleh memanggil super implementasi konstruktor yang sama karena operasi itu tidak akan menjamin bahwa kunci Anda lebih rendah. Anda bisa menggunakan: super(anotherMap.size()); putAll(anotherMap);sebagai gantinya.
sfera
Bagaimana jika Anda ingin nilai peta bukan-string? (yaitu CaseInsensitiveMap<String, Integer>)
Adam Parkin
16

Salah satu pendekatan adalah membuat subkelas kustom dari kelas Apache Commons AbstractHashedMap, mengesampingkan hashdan isEqualKeysmetode untuk melakukan hashing yang tidak sensitif huruf dan perbandingan kunci. (Catatan - Saya belum pernah mencoba ini sendiri ...)

Ini menghindari overhead untuk membuat objek baru setiap kali Anda perlu melakukan pencarian peta atau pembaruan. Dan Mapoperasi umum harus O (1) ... seperti biasa HashMap.

Dan jika Anda siap untuk menerima pilihan implementasi yang telah mereka buat, Apache Commons CaseInsensitiveMapmelakukan pekerjaan penyesuaian / spesialisasi AbstractHashedMapuntuk Anda.


Tetapi jika O (logN) getdan putoperasi dapat diterima, aTreeMap komparator string yang tidak sensitif adalah sebuah pilihan; misalnya menggunakan String.CASE_INSENSITIVE_ORDER.

Dan jika Anda tidak keberatan membuat objek String sementara baru setiap kali Anda melakukan putatau get, maka jawaban Vishal baik-baik saja. (Meskipun, saya perhatikan bahwa Anda tidak akan menyimpan kunci asli dari kunci jika Anda melakukannya ...)

Stephen C
sumber
6

Subkelas HashMapdan buat versi yang huruf kecil pada tombol putdanget (dan mungkin metode berorientasi kunci lainnya).

Atau komposit a HashMap ke dalam kelas baru dan delegasikan semuanya ke peta, tetapi terjemahkan kuncinya.

Jika Anda perlu menyimpan kunci asli, Anda bisa mempertahankan dua peta, atau menyimpan kunci asli beserta nilainya.

Dave Newton
sumber
Apakah maksud Anda melakukan String.toLowerCase () selama pencarian?
rs
@ user710178 Tidak hanya selama pencarian, tetapi selama penyimpanan juga.
Dave Newton
@ user710178 Oh, benar, seperti yang ditunjukkan oleh jawaban lain, ini sudah ada, jika Anda tidak keberatan dengan ketergantungan tambahan.
Dave Newton
@StephenC Jika memenuhi kebutuhan Anda, pasti; OP menentukan HashMap, jadi itu yang saya ikuti :) Oh, maksud Anda yang Commons; Saya melihat. Saya kira, selama Anda tidak memerlukannya digeneralisasikan (atau apakah mereka akhirnya memiliki obat generik sekarang?)
Dave Newton
1
Untuk JDK 8 dan di atasnya, Anda juga harus (setidaknya) menimpa putAll karena implementasinya telah berubah.
Steve N
4

Dua pilihan muncul di benak saya:

  1. Anda dapat menggunakan langsung s.toUpperCase().hashCode();sebagai kunciMap .
  2. Anda dapat menggunakan TreeMap<String>dengan kebiasaan Comparatoryang mengabaikan kasing.

Kalau tidak, jika Anda lebih suka solusi Anda, alih-alih mendefinisikan jenis baru String, saya lebih suka mengimplementasikan Peta baru dengan fungsi insensibilitas case yang diperlukan.

Gabriel Belingueres
sumber
3

Bukankah lebih baik "membungkus" String untuk menghafal kode hash. Dalam kelas normal, kode hashCode () adalah O (N) pertama kali dan kemudian O (1) karena disimpan untuk penggunaan di masa depan.

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

Ini akan memungkinkan Anda untuk menggunakan implementasi Hashtable di java dan memiliki O (1) hasCode ().

le-doude
sumber
3

Anda dapat menggunakan HashingStrategy berdasarkan Mapdari Eclipse Collections

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

Catatan: Saya adalah kontributor untuk Eclipse Collections.

Nikhil Nanivadekar
sumber
2

Berdasarkan jawaban lain, pada dasarnya ada dua pendekatan: subclassing HashMapatau pembungkus String. Yang pertama membutuhkan sedikit lebih banyak pekerjaan. Bahkan, jika Anda ingin melakukannya dengan benar, Anda harus mengganti hampir semua metode ( containsKey, entrySet, get, put, putAll and remove).

Bagaimanapun, ia memiliki masalah. Jika Anda ingin menghindari masalah di masa mendatang, Anda harus menentukan operasi Localedalam Stringkasus. Jadi, Anda akan membuat metode baru ( get(String, Locale), ...). Semuanya lebih mudah dan lebih jelas String pembungkus:

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
}

Dan yah, tentang kekhawatiran Anda pada kinerja: optimasi prematur adalah akar dari semua kejahatan :)

sinuhepop
sumber
2
"Dan, tentang kekhawatiran Anda pada kinerja: optimasi prematur adalah akar dari semua kejahatan :)" - Sebaliknya, menggunakannya sebagai alasan untuk selalu menulis kode yang tidak efisien adalah apa yang jahat.
Gordon
1
Sebenarnya @Gordon, keduanya sama-sama buruk, tergantung pada konteksnya. Label "jahat" adalah tanda pemikiran hitam-putih, seperti "praktik terbaik" dan berbagai frasa tidak membantu lainnya yang cenderung digunakan oleh banyak orang IT. Sebaiknya hindari sama sekali.
Stephen C
Saya mendapati bahwa memberi tahu orang-orang bahwa mereka tidak mengikuti "praktik terbaik" cenderung menghasilkan lebih sedikit penggalian daripada memberi tahu mereka bahwa mereka memiliki praktik buruk.
Gordon
0

Ini adalah adaptor untuk HashMaps yang saya terapkan untuk proyek baru-baru ini. Bekerja dengan cara yang sama dengan apa yang dilakukan @SandyR, tetapi merangkum logika konversi sehingga Anda tidak secara manual mengonversi string ke objek wrapper.

Saya menggunakan fitur Java 8 tetapi dengan beberapa perubahan, Anda dapat menyesuaikannya dengan versi sebelumnya. Saya mengujinya untuk skenario paling umum, kecuali fungsi Java 8 stream yang baru.

Pada dasarnya ia membungkus HashMap, mengarahkan semua fungsi ke sana sambil mengonversi string ke / dari objek wrapper. Tetapi saya juga harus mengadaptasi KeySet dan EntrySet karena mereka meneruskan beberapa fungsi ke peta itu sendiri. Jadi saya mengembalikan dua set baru untuk kunci dan entri yang sebenarnya membungkus keySet asli () dan entrySet ().

Satu catatan: Java 8 telah mengubah implementasi metode putAll yang saya tidak bisa menemukan cara mudah untuk menimpanya. Jadi implementasi saat ini mungkin menurunkan kinerja terutama jika Anda menggunakan putAll () untuk kumpulan data yang besar.

Harap beri tahu saya jika Anda menemukan bug atau memiliki saran untuk meningkatkan kode.

paket 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
sumber
0

Karena itu, saya membuat objek baru CaseInsensitiveString untuk setiap acara. Jadi, itu mungkin mengenai kinerja.

Membuat pembungkus atau mengonversi kunci ke huruf kecil sebelum mencari keduanya membuat objek baru. Menulis implementasi java.util.Map Anda sendiri adalah satu-satunya cara untuk menghindari ini. Itu tidak terlalu sulit, dan IMO sepadan. Saya menemukan fungsi hash berikut berfungsi dengan cukup baik, hingga beberapa ratus kunci.

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
sumber
-3

Bagaimana dengan menggunakan java 8 stream.

nodeMap.entrySet().stream().filter(x->x.getKey().equalsIgnoreCase(stringfromEven.toString()).collect(Collectors.toList())
Amarendra Reddy
sumber
Ini tidak memungkinkan Anda untuk mencari nilai di peta dengan cara case-insensitive.
Gili
equalsignorecase akan melakukannya bukan?
Amarendra Reddy
Anda sedang membangun daftar. OP meminta peta.
Gili
Ini menghancurkan manfaat kompleksitas peta O (1).
Paul Rooney