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?
Jawaban:
Itu benar-benar yang Anda butuhkan.
sumber
public static <K extends String, V> Map<K, V> caseInsensitiveMap() { return new TreeMap<K, V>(String.CASE_INSENSITIVE_ORDER); }
<K extends String>
karenaString
sudah final:public static <V> Map<String, V> caseInsensitiveMap() { return new TreeMap<String, V>(String.CASE_INSENSITIVE_ORDER); }
Seperti yang disarankan oleh Guido García dalam jawaban mereka di sini :
Atau
https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/map/CaseInsensitiveMap.html
sumber
containsKey()
danremove()
harus diganti dengan cara yang samaget()
. yangHashMap.putAll()
menggunakan implementasiput()
, sehingga seharusnya tidak menjadi masalah - selama HashMap pelaksanaan tetap sama. ;) jugaget()
metode tanda tangan mengambilObject
argumen, bukan aString
. kode juga tidak menguji kunci null:super.get(key == null ? null : key.toString().toLowercase());
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.CaseInsensitiveMap<String, Integer>
)Salah satu pendekatan adalah membuat subkelas kustom dari kelas Apache Commons
AbstractHashedMap
, mengesampingkanhash
danisEqualKeys
metode 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
Map
operasi umum harus O (1) ... seperti biasaHashMap
.Dan jika Anda siap untuk menerima pilihan implementasi yang telah mereka buat, Apache Commons
CaseInsensitiveMap
melakukan pekerjaan penyesuaian / spesialisasiAbstractHashedMap
untuk Anda.Tetapi jika O (logN)
get
danput
operasi dapat diterima, aTreeMap
komparator string yang tidak sensitif adalah sebuah pilihan; misalnya menggunakanString.CASE_INSENSITIVE_ORDER
.Dan jika Anda tidak keberatan membuat objek String sementara baru setiap kali Anda melakukan
put
atauget
, maka jawaban Vishal baik-baik saja. (Meskipun, saya perhatikan bahwa Anda tidak akan menyimpan kunci asli dari kunci jika Anda melakukannya ...)sumber
Subkelas
HashMap
dan buat versi yang huruf kecil pada tombolput
danget
(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.
sumber
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?)Dua pilihan muncul di benak saya:
s.toUpperCase().hashCode();
sebagai kunciMap
.TreeMap<String>
dengan kebiasaanComparator
yang 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.
sumber
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.
Ini akan memungkinkan Anda untuk menggunakan implementasi Hashtable di java dan memiliki O (1) hasCode ().
sumber
Anda dapat menggunakan HashingStrategy berdasarkan
Map
dari Eclipse CollectionsCatatan: Saya adalah kontributor untuk Eclipse Collections.
sumber
Berdasarkan jawaban lain, pada dasarnya ada dua pendekatan: subclassing
HashMap
atau pembungkusString
. 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
Locale
dalamString
kasus. Jadi, Anda akan membuat metode baru (get(String, Locale)
, ...). Semuanya lebih mudah dan lebih jelas String pembungkus:Dan yah, tentang kekhawatiran Anda pada kinerja: optimasi prematur adalah akar dari semua kejahatan :)
sumber
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;
sumber
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.
sumber
Bagaimana dengan menggunakan java 8 stream.
sumber