바이트 배열을 맵 키로 사용
바이트 배열을 맵 키로 사용하는 데 문제가 있습니까? 나는 또한 할 수 있고 new String(byte[])
해시 할 수있는 String
것이 더 간단합니다 byte[]
.
문제는 및 에 byte[]
대한 개체 ID를 사용 하여equals
hashCode
byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}
에서 일치하지 않습니다 HashMap
. 세 가지 옵션이 있습니다.
- 으로 래핑
String
하지만 인코딩 문제는 (바이트-> 호스트-> 바이트가 많은 바이트를 제공하는지 확인해야합니다). - 사용
List<Byte>
(메모리가 비쌀 수 있음). - 자체 래핑 클래스 를 수행하고 바이트 배열의 내용을 작성
hashCode
하고equals
사용하십시오.
키에 대한 참조 평등 만 원하는 한 괜찮습니다. 배열은 아마도 원하는 방식으로 "값 평등"을 구현하지 않습니다. 예를 들면 :
byte[] array1 = new byte[1];
byte[] array2 = new byte[1];
System.out.println(array1.equals(array2));
System.out.println(array1.hashCode());
System.out.println(array2.hashCode());
다음과 같이 인쇄합니다.
false
1671711
11394033
(실제 숫자는 관련이 없습니다. 서로 다르다는 사실이 중요합니다.)
평등 생성을 원 가정하면 포함 하고 byte[]
평등 및 해시 코드를 생성하게 구현 하는 자체 래퍼 만드는 것이 좋습니다 .
public final class ByteArrayWrapper
{
private final byte[] data;
public ByteArrayWrapper(byte[] data)
{
if (data == null)
{
throw new NullPointerException();
}
this.data = data;
}
@Override
public boolean equals(Object other)
{
if (!(other instanceof ByteArrayWrapper))
{
return false;
}
return Arrays.equals(data, ((ByteArrayWrapper)other).data);
}
@Override
public int hashCode()
{
return Arrays.hashCode(data);
}
}
를 ByteArrayWrapper
키로 저장 후 바이트 배열 내의 값을 변경하면 키 HashMap
를 다시 찾는 데 문제가 발생합니다 ... ByteArrayWrapper
원하는 경우 생성자 에서 데이터 복사본을 찾을 수 있습니다. 하지만 배열의 내용을 변경 하지 않을 것 입니다.
편집 :에서 언급했듯이 주석 ByteBuffer
이를 위해 사용할 수도 있습니다 (특히 방법). 필요하지 않은 모든 추가 능력을 감안할 때 그것이 있는지 옳은 것인지 모르겠지만 선택 사항입니다.ByteBuffer#wrap(byte[])
ByteBuffer
이를 위해 ByteBuffer를 사용할 수 있습니다. (기본적으로 비교기가있는 byte [] 래퍼입니다)
HashMap<ByteBuffer, byte[]> kvs = new HashMap<ByteBuffer, byte[]>();
byte[] k1 = new byte[]{1,2 ,3};
byte[] k2 = new byte[]{1,2 ,3};
byte[] val = new byte[]{12,23,43,4};
kvs.put(ByteBuffer.wrap(k1), val);
System.out.println(kvs.containsKey(ByteBuffer.wrap(k2)));
인쇄됩니다
true
사용할 수 있습니다 java.math.BigInteger
. 그것은 생성 해 튼다 BigInteger(byte[] val)
. 참조 유형 해시 테이블의 키로 사용할 수 있습니다. 그리고 .equals()
하고 .hashCode()
BigInteger의 바이트 [] 배열 일관 동등한 의미를 수단을 정수 숫자로 정의된다.
대답이 가장 간단한 대안을 지적하지 않는다는 것이 매우 놀랍습니다.
예, HashMap을 사용할 수는 없지만 SortedMap을 대안으로 사용하는 것을 막지합니다. 유일한 방법은 어레이를 비교해야하는 비교를 작성하는 것입니다. HashMap만큼 성능이 좋지는 않지만 대안을 표현 여기로 가십시오 (구현을 숨기려면 SortedMap을 맵으로 바꿀 수 있습니다).
private SortedMap<int[], String> testMap = new TreeMap<>(new ArrayComparator());
private class ArrayComparator implements Comparator<int[]> {
@Override
public int compare(int[] o1, int[] o2) {
int result = 0;
int maxLength = Math.max(o1.length, o2.length);
for (int index = 0; index < maxLength; index++) {
int o1Value = index < o1.length ? o1[index] : 0;
int o2Value = index < o2.length ? o2[index] : 0;
int cmp = Integer.compare(o1Value, o2Value);
if (cmp != 0) {
result = cmp;
break;
}
}
return result;
}
}
이 구현은 다른 배열에 적용 할 수 있습니다.주의 할 유일한 사항은 동일한 배열 (= 동일한 멤버의 동일한 길이)이 0을 반환해야하며 결정적 순서가 것입니다.
자바의 배열이 반드시 hashCode()
및 equals(Object)
메소드를 구현으로 구현하는 것이 아니라고 생각합니다 . 즉, 두 개의 두 개의 바이트 배열이 반드시 반드시 올바른 해시 코드를 공유하는 것은 절대로 동일합니다. 이 두 가지 특성이 조화를 이루며 HashMap이 작동합니다.
따라서, 추천 나는 에 사용 byte[]
는의 HashMap의 키로서.
ByteArrKey와 같은 클래스를 만들고 코드와 동일한 메서드를 오버로드해야하며 둘 사이의 계약을 기억하십시오.
특히 다른 바이트 버퍼에서 일부 부분 만 복사하는 경우 바이트 배열 끝에 추가되는 항목을 건너 뛰므로 유연성이 향상됩니다.
이렇게하면 두 가지 방법을 동일하게 수행 할 수 있습니다.
기본 배열 대신 Arrays.equals 및 Array.hashCode를 사용하기 때문에 문제가 발생합니다.
Arrays.toString (바이트)
Base32 또는 Base64를 사용하여 byte []를 '안전한'문자열로 변환 할 수도 있습니다. 예를 들면 다음과 같습니다.
byte[] keyValue = new byte[] {…};
String key = javax.xml.bind.DatatypeConverter.printBase64Binary(keyValue);
물론 위의 여러 변형이 있습니다.
String key = org.apache.commons.codec.binary.Base64.encodeBase64(keyValue);
다음은 TreeMap, Comparator 인터페이스 및 Java 메소드를 사용하는 솔루션입니다. java.util.Arrays.equals (바이트 [], 바이트 []);
참고 :지도의 순서는 방법과 관련이 없습니다.
SortedMap<byte[], String> testMap = new TreeMap<>(new ArrayComparator());
static class ArrayComparator implements Comparator<byte[]> {
@Override
public int compare(byte[] byteArray1, byte[] byteArray2) {
int result = 0;
boolean areEquals = Arrays.equals(byteArray1, byteArray2);
if (!areEquals) {
result = -1;
}
return result;
}
}
또한 다음과 같이 사용자 지정 ByteHashMap을 만들 수 있습니다.
ByteHashMap byteMap = new ByteHashMap();
byteMap.put(keybyteArray,valueByteArray);
다음은 완전한 구현입니다.
public class ByteHashMap implements Map<byte[], byte[]>, Cloneable,
Serializable {
private Map<ByteArrayWrapper, byte[]> internalMap = new HashMap<ByteArrayWrapper, byte[]>();
public void clear() {
internalMap.clear();
}
public boolean containsKey(Object key) {
if (key instanceof byte[])
return internalMap.containsKey(new ByteArrayWrapper((byte[]) key));
return internalMap.containsKey(key);
}
public boolean containsValue(Object value) {
return internalMap.containsValue(value);
}
public Set<java.util.Map.Entry<byte[], byte[]>> entrySet() {
Iterator<java.util.Map.Entry<ByteArrayWrapper, byte[]>> iterator = internalMap
.entrySet().iterator();
HashSet<Entry<byte[], byte[]>> hashSet = new HashSet<java.util.Map.Entry<byte[], byte[]>>();
while (iterator.hasNext()) {
Entry<ByteArrayWrapper, byte[]> entry = iterator.next();
hashSet.add(new ByteEntry(entry.getKey().data, entry
.getValue()));
}
return hashSet;
}
public byte[] get(Object key) {
if (key instanceof byte[])
return internalMap.get(new ByteArrayWrapper((byte[]) key));
return internalMap.get(key);
}
public boolean isEmpty() {
return internalMap.isEmpty();
}
public Set<byte[]> keySet() {
Set<byte[]> keySet = new HashSet<byte[]>();
Iterator<ByteArrayWrapper> iterator = internalMap.keySet().iterator();
while (iterator.hasNext()) {
keySet.add(iterator.next().data);
}
return keySet;
}
public byte[] put(byte[] key, byte[] value) {
return internalMap.put(new ByteArrayWrapper(key), value);
}
@SuppressWarnings("unchecked")
public void putAll(Map<? extends byte[], ? extends byte[]> m) {
Iterator<?> iterator = m.entrySet().iterator();
while (iterator.hasNext()) {
Entry<? extends byte[], ? extends byte[]> next = (Entry<? extends byte[], ? extends byte[]>) iterator
.next();
internalMap.put(new ByteArrayWrapper(next.getKey()), next
.getValue());
}
}
public byte[] remove(Object key) {
if (key instanceof byte[])
return internalMap.remove(new ByteArrayWrapper((byte[]) key));
return internalMap.remove(key);
}
public int size() {
return internalMap.size();
}
public Collection<byte[]> values() {
return internalMap.values();
}
private final class ByteArrayWrapper {
private final byte[] data;
public ByteArrayWrapper(byte[] data) {
if (data == null) {
throw new NullPointerException();
}
this.data = data;
}
public boolean equals(Object other) {
if (!(other instanceof ByteArrayWrapper)) {
return false;
}
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
}
public int hashCode() {
return Arrays.hashCode(data);
}
}
private final class ByteEntry implements Entry<byte[], byte[]> {
private byte[] value;
private byte[] key;
public ByteEntry(byte[] key, byte[] value) {
this.key = key;
this.value = value;
}
public byte[] getKey() {
return this.key;
}
public byte[] getValue() {
return this.value;
}
public byte[] setValue(byte[] value) {
this.value = value;
return value;
}
}
}
참고 URL : https://stackoverflow.com/questions/1058149/using-a-byte-array-as-map-key
'ProgramingTip' 카테고리의 다른 글
Typescript를 사용하여 Express Request 객체 확장 (0) | 2020.10.28 |
---|---|
jQuery가 다른 Javascript 프레임 워크에 비해 채택되는 이유는 무엇입니까? (0) | 2020.10.28 |
선택을 반복하고 선택하거나 선택하지 않는 각을 계산합니다. (0) | 2020.10.28 |
Go에서 파일에 추가 (0) | 2020.10.28 |
자바 펼쳐는 해시 값으로 페이지를 다시로드합니다. (0) | 2020.10.28 |