Java中如何使用?byte?數(shù)組作為?Map?的?key
本文將引領(lǐng)我們探索:如何將byte數(shù)組作為HashMap中鍵。HashMap的機制使我們無法直接這樣做。讓我們研究一下,為何出現(xiàn)此狀況,以及針對這種情況,幾種可供選擇的解決方案。
HashMap的工作原理
HashMap是一種使用哈希機制來存儲和檢索值的數(shù)據(jù)結(jié)構(gòu)。使用哈希碼來存儲和檢索值可以大大提高HashMap的性能,因為它可以使查找鍵值對的時間復(fù)雜度保持在 O ( 1 ) O(1) O(1)的級別。當(dāng)然,這也要求我們在實現(xiàn)hashCode()方法時盡可能地讓哈希碼分布均勻,以免造成哈希沖突,從而影響HashMap的效率。
當(dāng)我們調(diào)用put(key, value)方法時,HashMap會通過鍵的hashCode()方法計算哈希碼。這個哈希碼用于確定最終存儲值的桶:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(hash(key), key)) == null ? null : e.value;
}在使用get(key)方法檢索值時,需要經(jīng)過一系列處理步驟:首先,會通過鍵計算哈希碼,然后找到哈希桶。接下來,使用equals()方法檢查桶中的每個條目是否與鍵相等。最終,返回匹配條目的值:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}equals和hashCode方法
在Java編程中,equals方法和hashCode方法都有應(yīng)該遵守的規(guī)則。在HashMap這個數(shù)據(jù)結(jié)構(gòu)中,有一個方面尤其重要:具有相同equals方法比較結(jié)果的對象,必須返回相同的哈希值。然而,反之則不一定成立,也就是說,具有相同哈希值的對象,并不一定具有相同的equals方法比較結(jié)果。這也是為什么我們可以將多個對象存儲在HashMap的同一個桶中的原因。
在使用HashMap時,建議不要更改key的哈希值。雖然這不是強制性規(guī)定,但強烈建議將鍵定義為不可變對象。如果對象是不可變的,無論hashCode方法的實現(xiàn)如何,它的哈希值都不會被更改。
在默認(rèn)情況下,哈希值是基于對象的所有字段進行計算的。如果我們需要使用可變的鍵,我們需要重寫hashCode方法,以確保它的計算不涉及可變字段。為了維護這一個規(guī)則,我們還需要修改equals方法。
使用 byte 數(shù)組作為key
為了能夠從映射中成功地檢索值,相等性必須是有意義的。這就是使用byte數(shù)組并不是一個真正的選擇的主要原因。在Java中,數(shù)組使用對象標(biāo)識來確定相等性。如果我們使用byte數(shù)組作為key創(chuàng)建HashMap,那么只有使用完全相同的數(shù)組對象才能檢索值。
讓我們使用byte數(shù)組作為key創(chuàng)建一個簡單的例子:
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}));我們雖然有兩個相同的鍵,但是我們無法使用具有相同值的新創(chuàng)建的數(shù)組檢索到任何內(nèi)容,運行結(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)建一個字符串非常簡單。我們可以使用Base64輕松編碼和解碼字符串,然后創(chuàng)建一個使用字符串作為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})));運行結(jié)果如下:
value2
value2
value2
注意: 在byte數(shù)組轉(zhuǎn)化為String時會有性能損耗。因此,在大多數(shù)情況下,該解決方案并不推薦。
使用List
與String類似,List#equals方法將檢查其每個元素的相等性:
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實現(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)));
運行結(jié)果如下:
value2
value2
value2
注意: Byte對象的列表將占用比byte數(shù)組更多的內(nèi)存。因此,在大多數(shù)情況下,該解決方案并不推薦。
自定義類(推薦使用)
我們還可以自己的定義一個類,用來完全控制哈希碼計算和相等性。這樣,我們可以確保解決方案快速,并且沒有太大的內(nèi)存占用。
讓我們創(chuàng)建一個只有一個final私有byte數(shù)組字段的類。它將沒有setter方法,只用getter方法,用來確保完全不可變性。
然后在實現(xiàn)自己的equals和hashCode方法。為了方法,我們可以使用Arrays類來完成這兩項任務(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})));運行結(jié)果如下:
value2
value2
value2
注意: 自定義的類既沒有轉(zhuǎn)化為String的性能損耗,也沒有Byte對象列表的內(nèi)存占用。因此,該解決方案推薦使用。
總結(jié)
本文將討論在使用HashMap時,當(dāng)byte數(shù)組作為key時所遇到的問題及其解決方案。
首先,我們將研究為什么不能直接使用數(shù)組作為鍵。在使用HashMap時,我們需要保證每個鍵的唯一性,而使用數(shù)組作為鍵可能會出現(xiàn)沖突。這是因為數(shù)組的hashCode值是基于其在內(nèi)存中的地址計算得出的,因此即使兩個數(shù)組內(nèi)容完全相同,它們在內(nèi)存中的位置不同,它們的hashCode也會不同。因此,直接使用數(shù)組作為鍵可能會導(dǎo)致無法正確獲取值或者出現(xiàn)意外的覆蓋。
接著,我們會介紹使用String和List這兩種數(shù)據(jù)結(jié)構(gòu)作為臨時解決方案的方法。它們都是具有可比性和可哈希性的數(shù)據(jù)結(jié)構(gòu),能夠保證唯一性。但這種方法并不是完美的解決方案,因為使用String或List作為鍵會帶來一些性能上的開銷,或者占用不必要的內(nèi)存空間。
最后,我們將通過自定義類的方式完美解決這個問題。這個自定義類包含了一個byte數(shù)組字段,并重寫hashCode和equals方法,以確保唯一性和正確性。通過這種方式,我們可以避免使用String或List時的性能和內(nèi)存占用問題,并且能夠在保證正確性的同時獲得更高的效率。
到此這篇關(guān)于在Java中使用 byte 數(shù)組作為 Map 的 key的文章就介紹到這了,更多相關(guān)Java使用 byte 數(shù)組作為 Map 的 key內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java 中HttpURLConnection附件上傳的實例詳解
這篇文章主要介紹了Java 中HttpURLConnection附件上傳的實例詳解的相關(guān)資料,希望通過本文大家能掌握這樣的知識內(nèi)容,需要的朋友可以參考下2017-09-09
Spring?Native打包本地鏡像的操作方法(無需通過Graal的maven插件buildtools)
這篇文章主要介紹了Spring?Native打包本地鏡像,無需通過Graal的maven插件buildtools,本文探索一下,如果不通過這個插件來生成鏡像,結(jié)合實例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2023-02-02
SpringBoot整合rabbitMq自定義消息轉(zhuǎn)換方式
這篇文章主要介紹了SpringBoot整合rabbitMq自定義消息轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-09-09
Java基于動態(tài)規(guī)劃法實現(xiàn)求最長公共子序列及最長公共子字符串示例
這篇文章主要介紹了Java基于動態(tài)規(guī)劃法實現(xiàn)求最長公共子序列及最長公共子字符串,簡單描述了動態(tài)規(guī)劃法的概念、原理,并結(jié)合實例形式分析了Java使用動態(tài)規(guī)劃法求最長公共子序列以及最長公共子字符串相關(guān)實現(xiàn)技巧,需要的朋友可以參考下2018-08-08
使用java代碼實現(xiàn)保留小數(shù)點的位數(shù)
因為個人應(yīng)用的需要,所以就寫個簡單點的了。希望大家都給給建議,共同學(xué)習(xí)。需要的朋友也可以參考下2013-07-07

