1. Gambaran keseluruhan
Dalam artikel ini, kami akan meneroka pelaksanaan dalaman kelas LinkedHashMap . LinkedHashMap adalah pelaksanaan umum antara muka Peta .
Pelaksanaan khusus ini adalah subkelas HashMap dan oleh itu berkongsi asas pembinaan pelaksanaan HashMap . Hasilnya, sangat disarankan untuk mengatasinya sebelum meneruskan artikel ini.
2. LinkedHashMap vs HashMap
The LinkedHashMap kelas adalah sangat serupa dengan HashMap dalam kebanyakan aspek. Walau bagaimanapun, peta hash yang dihubungkan berdasarkan pada jadual hash dan senarai terpaut untuk meningkatkan fungsi peta hash.
Ia mengekalkan senarai yang mempunyai dua kaitan yang berjalan melalui semua entri di samping pelbagai ukuran asas lalai 16.
Untuk mengekalkan susunan unsur-unsur, suai hashmap mengaitkan Map.Entry kelas HashMap dengan menambah petunjuk kepada entri seterusnya dan sebelumnya:
static class Entry extends HashMap.Node { Entry before, after; Entry(int hash, K key, V value, Node next) { super(hash, key, value, next); } }
Perhatikan bahawa kelas Entri hanya menambah dua petunjuk; sebelum dan selepasnya membolehkannya menghubungkan diri dengan senarai terpaut. Selain itu, ia menggunakan pelaksanaan kelas Entry dari HashMap.
Akhirnya, ingat bahawa senarai terpaut ini menentukan urutan lelaran, yang secara lalai adalah susunan penyisipan elemen (susunan-susunan).
3. Pemetaan -Susunan BerkaitanHashMap
Mari kita lihat contoh peta hash yang dipautkan yang memerintahkan kemasukannya mengikut bagaimana mereka dimasukkan ke dalam peta. Ini juga menjamin bahawa pesanan ini akan dipelihara sepanjang kitaran hidup peta:
@Test public void givenLinkedHashMap_whenGetsOrderedKeyset_thenCorrect() { LinkedHashMap map = new LinkedHashMap(); map.put(1, null); map.put(2, null); map.put(3, null); map.put(4, null); map.put(5, null); Set keys = map.keySet(); Integer[] arr = keys.toArray(new Integer[0]); for (int i = 0; i < arr.length; i++) { assertEquals(new Integer(i + 1), arr[i]); } }
Di sini, kami hanya membuat ujian asas dan tidak konklusif mengenai susunan entri dalam peta hash yang dipautkan.
Kami dapat menjamin bahawa ujian ini akan selalu lulus kerana pesanan penyisipan akan selalu dipelihara. Kami tidak dapat membuat jaminan yang sama untuk HashMap.
Atribut ini dapat memberi keuntungan besar dalam API yang menerima peta apa pun, membuat salinan untuk memanipulasi dan mengembalikannya ke kod panggilan. Sekiranya pelanggan memerlukan peta yang dikembalikan untuk dipesan dengan cara yang sama sebelum memanggil API, maka hashmap yang dipautkan adalah cara untuk pergi.
Urutan penyisipan tidak akan terjejas jika kunci dimasukkan semula ke dalam peta.
4. Access-Order LinkedHashMap
LinkedHashMap menyediakan konstruktor khas yang membolehkan kita menentukan, antara faktor beban khusus (LF) dan kapasiti awal, mekanisme / strategi pesanan yang berbeza yang disebut akses-pesanan :
LinkedHashMap map = new LinkedHashMap(16, .75f, true);
Parameter pertama adalah kapasiti awal, diikuti oleh faktor beban dan param terakhir adalah mod pesanan . Oleh itu, dengan memasukkan true , kami mengaktifkan access-order, sedangkan defaultnya adalah insertion-order.
Mekanisme ini memastikan bahawa urutan iterasi elemen adalah urutan di mana unsur-unsur terakhir diakses, dari yang paling baru diakses ke yang paling baru diakses.
Oleh itu, membina cache Paling Baru Digunakan (LRU) agak mudah dan praktikal dengan peta seperti ini. Operasi put atau get berjaya menghasilkan akses untuk entri:
@Test public void givenLinkedHashMap_whenAccessOrderWorks_thenCorrect() { LinkedHashMap map = new LinkedHashMap(16, .75f, true); map.put(1, null); map.put(2, null); map.put(3, null); map.put(4, null); map.put(5, null); Set keys = map.keySet(); assertEquals("[1, 2, 3, 4, 5]", keys.toString()); map.get(4); assertEquals("[1, 2, 3, 5, 4]", keys.toString()); map.get(1); assertEquals("[2, 3, 5, 4, 1]", keys.toString()); map.get(3); assertEquals("[2, 5, 4, 1, 3]", keys.toString()); }
Perhatikan bagaimana susunan elemen dalam set kunci diubah ketika kita melakukan operasi akses di peta.
Sederhananya, setiap operasi akses pada peta menghasilkan urutan sehingga elemen yang diakses akan muncul terakhir jika lelaran akan segera dilakukan.
Selepas contoh di atas, jelaslah bahawa operasi putAll menghasilkan satu akses masuk untuk setiap pemetaan dalam peta yang ditentukan.
Secara semula jadi, lelaran pada paparan peta tidak mempengaruhi susunan lelaran peta sandaran; hanya operasi akses eksplisit di peta yang akan mempengaruhi pesanan .
LinkedHashMap juga menyediakan mekanisme untuk mempertahankan jumlah pemetaan tetap dan terus menurunkan entri tertua sekiranya yang baru perlu ditambahkan.
The removeEldestEntry kaedah mungkin diatasi untuk menguatkuasakan dasar ini untuk mengeluarkan pemetaan basi secara automatik.
Untuk melihat ini dalam praktik, marilah kita membuat kelas peta hash yang dipautkan sendiri, untuk satu-satunya tujuan menegakkan penghapusan pemetaan basi dengan memperluas LinkedHashMap :
public class MyLinkedHashMap extends LinkedHashMap { private static final int MAX_ENTRIES = 5; public MyLinkedHashMap( int initialCapacity, float loadFactor, boolean accessOrder) { super(initialCapacity, loadFactor, accessOrder); } @Override protected boolean removeEldestEntry(Map.Entry eldest) { return size() > MAX_ENTRIES; } }
Penggantian kami di atas akan membolehkan peta berkembang dengan ukuran maksimum 5 entri. Apabila ukuran melebihi itu, setiap entri baru akan dimasukkan dengan kos kehilangan entri sulung dalam peta iaitu entri yang masa akses terakhir mendahului semua entri lain:
@Test public void givenLinkedHashMap_whenRemovesEldestEntry_thenCorrect() { LinkedHashMap map = new MyLinkedHashMap(16, .75f, true); map.put(1, null); map.put(2, null); map.put(3, null); map.put(4, null); map.put(5, null); Set keys = map.keySet(); assertEquals("[1, 2, 3, 4, 5]", keys.toString()); map.put(6, null); assertEquals("[2, 3, 4, 5, 6]", keys.toString()); map.put(7, null); assertEquals("[3, 4, 5, 6, 7]", keys.toString()); map.put(8, null); assertEquals("[4, 5, 6, 7, 8]", keys.toString()); }
Perhatikan bagaimana entri tertua di awal set kunci terus menurun ketika kami menambahkan yang baru ke peta.
5. Pertimbangan Prestasi
Sama seperti HashMap , LinkedHashMap melakukan operasi Peta asas menambah, membuang dan memuat dalam masa tetap, selagi fungsi hash dimensi dengan baik. Ia juga menerima kunci kosong dan nilai nol.
Walau bagaimanapun, prestasi LinkedHashMap masa tetap ini cenderung sedikit lebih buruk daripada masa tetap HashMap kerana overhead tambahan untuk mengekalkan senarai berganda-ganda.
Pengulangan pandangan koleksi LinkedHashMap juga memerlukan masa linear O (n) yang serupa dengan HashMap . Di sisi lain, prestasi masa linear LinkedHashMap semasa lelaran lebih baik daripada masa linear HashMap .
Ini kerana, untuk LinkedHashMap , n di O (n) hanyalah bilangan entri dalam peta tanpa mengira kapasitinya. Manakala, untuk HashMap , n adalah kapasiti dan ukurannya dijumlahkan, O (ukuran + kapasiti).
Faktor Beban dan Kapasiti Permulaan ditentukan tepat seperti untuk HashMap . Walau bagaimanapun, perhatikan bahawa hukuman untuk memilih nilai yang terlalu tinggi untuk kapasiti awal kurang berat untuk LinkedHashMap daripada untuk HashMap , kerana masa lelaran untuk kelas ini tidak dipengaruhi oleh kapasiti.
6. Serentak
Sama seperti HashMap , pelaksanaan LinkedHashMap tidak diselaraskan. Oleh itu, jika anda akan mengaksesnya dari pelbagai utas dan sekurang-kurangnya salah satu daripada benang ini mungkin mengubahnya secara struktur, maka ia mesti disegerakkan secara luaran.
Sebaiknya lakukan ini semasa membuat:
Map m = Collections.synchronizedMap(new LinkedHashMap());
Perbezaan dengan HashMap terletak pada apa yang memerlukan pengubahsuaian struktur. Dalam peta hash yang dipautkan berdasarkan akses, hanya memanggil hasil API get dalam pengubahsuaian struktur . Di samping itu, terdapat operasi seperti meletakkan dan membuang .
7. Kesimpulannya
Dalam artikel ini, kami telah menjelajahi kelas Java LinkedHashMap sebagai salah satu implementasi utama antara muka Peta dari segi penggunaan. Kami juga telah meneroka cara kerjanya dari segi perbezaan dari HashMap yang merupakan superclassnya.
Mudah-mudahan, setelah membaca catatan ini, anda dapat membuat keputusan yang lebih tepat dan berkesan mengenai pelaksanaan Peta yang akan digunakan dalam kes penggunaan anda.
Kod sumber lengkap untuk semua contoh yang digunakan dalam artikel ini boleh didapati dalam projek GitHub.