Java中如何使用?byte?數(shù)組作為?Map?的?key
本文將引領(lǐng)我們探索:如何將byte數(shù)組作為HashMap中鍵。HashMap的機(jī)制使我們無法直接這樣做。讓我們研究一下,為何出現(xiàn)此狀況,以及針對這種情況,幾種可供選擇的解決方案。
HashMap的工作原理
HashMap是一種使用哈希機(jī)制來存儲(chǔ)和檢索值的數(shù)據(jù)結(jié)構(gòu)。使用哈希碼來存儲(chǔ)和檢索值可以大大提高HashMap的性能,因?yàn)樗梢允共檎益I值對的時(shí)間復(fù)雜度保持在 O ( 1 ) O(1) O(1)的級別。當(dāng)然,這也要求我們在實(shí)現(xiàn)hashCode()
方法時(shí)盡可能地讓哈希碼分布均勻,以免造成哈希沖突,從而影響HashMap的效率。
當(dāng)我們調(diào)用put(key, value)
方法時(shí),HashMap會(huì)通過鍵的hashCode()
方法計(jì)算哈希碼。這個(gè)哈希碼用于確定最終存儲(chǔ)值的桶:
public V get(Object key) { Node<K,V> e; return (e = getNode(hash(key), key)) == null ? null : e.value; }
在使用get(key)
方法檢索值時(shí),需要經(jīng)過一系列處理步驟:首先,會(huì)通過鍵計(jì)算哈希碼,然后找到哈希桶。接下來,使用equals()
方法檢查桶中的每個(gè)條目是否與鍵相等。最終,返回匹配條目的值:
public V put(K key, V value) { return putVal(hash(key), key, value, false, true); }
equals
和hashCode
方法
在Java編程中,equals
方法和hashCode
方法都有應(yīng)該遵守的規(guī)則。在HashMap這個(gè)數(shù)據(jù)結(jié)構(gòu)中,有一個(gè)方面尤其重要:具有相同equals
方法比較結(jié)果的對象,必須返回相同的哈希值。然而,反之則不一定成立,也就是說,具有相同哈希值的對象,并不一定具有相同的equals
方法比較結(jié)果。這也是為什么我們可以將多個(gè)對象存儲(chǔ)在HashMap的同一個(gè)桶中的原因。
在使用HashMap時(shí),建議不要更改key的哈希值。雖然這不是強(qiáng)制性規(guī)定,但強(qiáng)烈建議將鍵定義為不可變對象。如果對象是不可變的,無論hashCode
方法的實(shí)現(xiàn)如何,它的哈希值都不會(huì)被更改。
在默認(rèn)情況下,哈希值是基于對象的所有字段進(jìn)行計(jì)算的。如果我們需要使用可變的鍵,我們需要重寫hashCode
方法,以確保它的計(jì)算不涉及可變字段。為了維護(hù)這一個(gè)規(guī)則,我們還需要修改equals方法。
使用 byte 數(shù)組作為key
為了能夠從映射中成功地檢索值,相等性必須是有意義的。這就是使用byte數(shù)組并不是一個(gè)真正的選擇的主要原因。在Java中,數(shù)組使用對象標(biāo)識來確定相等性。如果我們使用byte數(shù)組作為key創(chuàng)建HashMap,那么只有使用完全相同的數(shù)組對象才能檢索值。
讓我們使用byte數(shù)組作為key創(chuàng)建一個(gè)簡單的例子:
byte[] key1 = {1, 2, 3}; byte[] key2 = {1, 2, 3}; Map<byte[], String> map = new HashMap<>(); map.put(key1, "value1"); map.put(key2, "value2"); System.out.println(map.get(key1)); System.out.println(map.get(key2)); System.out.println(map.get(new byte[]{1, 2, 3}));
我們雖然有兩個(gè)相同的鍵,但是我們無法使用具有相同值的新創(chuàng)建的數(shù)組檢索到任何內(nèi)容,運(yùn)行結(jié)果如下:
value1
value2
null
解決方法
使用String
String
的相等性基于字符數(shù)組的內(nèi)容:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
字符串也是不可變的,并且基于byte數(shù)組創(chuàng)建一個(gè)字符串非常簡單。我們可以使用Base64輕松編碼和解碼字符串,然后創(chuàng)建一個(gè)使用字符串作為key而不是byte數(shù)組的HashMap:
String key1 = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); String key2 = Base64.getEncoder().encodeToString(new byte[]{1, 2, 3}); Map<String, String> map = new HashMap<>(); map.put(key1, "value1"); map.put(key2, "value2"); System.out.println(map.get(key1)); System.out.println(map.get(key2)); System.out.println(map.get(Base64.getEncoder().encodeToString(new byte[]{1, 2, 3})));
運(yùn)行結(jié)果如下:
value2
value2
value2
注意: 在byte數(shù)組轉(zhuǎn)化為String
時(shí)會(huì)有性能損耗。因此,在大多數(shù)情況下,該解決方案并不推薦。
使用List
與String
類似,List#equals
方法將檢查其每個(gè)元素的相等性:
public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof List)) return false; ListIterator<E> e1 = listIterator(); ListIterator<?> e2 = ((List<?>) o).listIterator(); while (e1.hasNext() && e2.hasNext()) { E o1 = e1.next(); Object o2 = e2.next(); if (!(o1==null ? o2==null : o1.equals(o2))) return false; } return !(e1.hasNext() || e2.hasNext()); }
如果這些元素具有合理的equals()方法并且是不可變的,則List將作為HashMap鍵正確工作。我們只需要確保使用不可變的List實(shí)現(xiàn):
List<Byte> key1 = ImmutableList.of((byte) 1, (byte) 2, (byte) 3); List<Byte> key2 = ImmutableList.of((byte) 1, (byte) 2, (byte) 3); Map<List<Byte>, String> map = new HashMap<>(); map.put(key1, "value1"); map.put(key2, "value2"); System.out.println(map.get(key1)); System.out.println(map.get(key2)); System.out.println(map.get(ImmutableList.of((byte) 1, (byte) 2, (byte) 3)));
運(yùn)行結(jié)果如下:
value2
value2
value2
注意: Byte對象的列表將占用比byte數(shù)組更多的內(nèi)存。因此,在大多數(shù)情況下,該解決方案并不推薦。
自定義類(推薦使用)
我們還可以自己的定義一個(gè)類,用來完全控制哈希碼計(jì)算和相等性。這樣,我們可以確保解決方案快速,并且沒有太大的內(nèi)存占用。
讓我們創(chuàng)建一個(gè)只有一個(gè)final
私有byte數(shù)組字段的類。它將沒有setter方法,只用getter方法,用來確保完全不可變性。
然后在實(shí)現(xiàn)自己的equals
和hashCode
方法。為了方法,我們可以使用Arrays
類來完成這兩項(xiàng)任務(wù),最終代碼如下:
public class BytesKey { private final byte[] array; public BytesKey(byte[] array) { this.array = array; } public byte[] getArray() { return array.clone(); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (o == null || getClass() != o.getClass()){ return false; } BytesKey bytesKey = (BytesKey) o; return Arrays.equals(array, bytesKey.array); } @Override public int hashCode() { return Arrays.hashCode(array); } }
最后,我們使用我們自定義的類作為HashMap的key:
BytesKey key1 = new BytesKey(new byte[]{1, 2, 3}); BytesKey key2 = new BytesKey(new byte[]{1, 2, 3}); Map<BytesKey, String> map = new HashMap<>(); map.put(key1, "value1"); map.put(key2, "value2"); System.out.println(map.get(key1)); System.out.println(map.get(key2)); System.out.println(map.get(new BytesKey(new byte[]{1, 2, 3})));
運(yùn)行結(jié)果如下:
value2
value2
value2
注意: 自定義的類既沒有轉(zhuǎn)化為String
的性能損耗,也沒有Byte對象列表的內(nèi)存占用。因此,該解決方案推薦使用。
總結(jié)
本文將討論在使用HashMap時(shí),當(dāng)byte數(shù)組作為key時(shí)所遇到的問題及其解決方案。
首先,我們將研究為什么不能直接使用數(shù)組作為鍵。在使用HashMap時(shí),我們需要保證每個(gè)鍵的唯一性,而使用數(shù)組作為鍵可能會(huì)出現(xiàn)沖突。這是因?yàn)閿?shù)組的hashCode值是基于其在內(nèi)存中的地址計(jì)算得出的,因此即使兩個(gè)數(shù)組內(nèi)容完全相同,它們在內(nèi)存中的位置不同,它們的hashCode也會(huì)不同。因此,直接使用數(shù)組作為鍵可能會(huì)導(dǎo)致無法正確獲取值或者出現(xiàn)意外的覆蓋。
接著,我們會(huì)介紹使用String和List這兩種數(shù)據(jù)結(jié)構(gòu)作為臨時(shí)解決方案的方法。它們都是具有可比性和可哈希性的數(shù)據(jù)結(jié)構(gòu),能夠保證唯一性。但這種方法并不是完美的解決方案,因?yàn)槭褂肧tring或List作為鍵會(huì)帶來一些性能上的開銷,或者占用不必要的內(nèi)存空間。
最后,我們將通過自定義類的方式完美解決這個(gè)問題。這個(gè)自定義類包含了一個(gè)byte數(shù)組字段,并重寫hashCode
和equals
方法,以確保唯一性和正確性。通過這種方式,我們可以避免使用String或List時(shí)的性能和內(nèi)存占用問題,并且能夠在保證正確性的同時(shí)獲得更高的效率。
到此這篇關(guān)于在Java中使用 byte 數(shù)組作為 Map 的 key的文章就介紹到這了,更多相關(guān)Java使用 byte 數(shù)組作為 Map 的 key內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Kotlin語法學(xué)習(xí)-變量定義、函數(shù)擴(kuò)展、Parcelable序列化等簡單總結(jié)
這篇文章主要介紹了Kotlin語法學(xué)習(xí)-變量定義、函數(shù)擴(kuò)展、Parcelable序列化等簡單總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-05-05Java利用DelayQueue實(shí)現(xiàn)延遲任務(wù)代碼實(shí)例
這篇文章主要介紹了Java利用DelayQueue實(shí)現(xiàn)延遲任務(wù)代碼實(shí)例,DelayQueue?是一個(gè)支持延時(shí)獲取元素的阻塞隊(duì)列,?內(nèi)部采用優(yōu)先隊(duì)列?PriorityQueue?存儲(chǔ)元素,同時(shí)元素必須實(shí)現(xiàn)?Delayed?接口,需要的朋友可以參考下2023-12-12Springboot 接收POST、json、文本數(shù)據(jù)的方法 附示例
這篇文章主要介紹了Springboot 接收POST、json、文本數(shù)據(jù)實(shí)踐,如果把 json 作為參數(shù)傳遞,我們可以使用 @requestbody 接收參數(shù),將數(shù)據(jù)直接轉(zhuǎn)換成對象,本文通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-10-10