ProgramingTip

바이트 배열을 맵 키로 사용

bestdevel 2020. 10. 28. 20:20
반응형

바이트 배열을 맵 키로 사용


바이트 배열을 맵 키로 사용하는 데 문제가 있습니까? 나는 또한 할 수 있고 new String(byte[])해시 할 수있는 String것이 더 간단합니다 byte[].


문제는 byte[]대한 개체 ID를 사용 하여equalshashCode

byte[] b1 = {1, 2, 3}
byte[] b2 = {1, 2, 3}

에서 일치하지 않습니다 HashMap. 세 가지 옵션이 있습니다.

  1. 으로 래핑 String하지만 인코딩 문제는 (바이트-> 호스트-> 바이트가 많은 바이트를 제공하는지 확인해야합니다).
  2. 사용 List<Byte>(메모리가 비쌀 수 있음).
  3. 자체 래핑 클래스 를 수행하고 바이트 배열의 내용을 작성 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

반응형