Looking at Map keys are not good if they are arranged in an array, there are various ways to make it work while thinking that it will not work. Notes on using byte array as key in Java Map is linked, and [Using a byte array as Map key](http: / /stackoverflow.com/questions/1058149/using-a-byte-array-as-map-key) has a link, and the last Stack Overflow has a lot of it.
Here, it is HashMap. It should change with TreeMap. → How to use arrays for TreeMap keys The key premise is --Implementing equals --Implementing hashCode --Immutable instance
The first and second are used perfectly with containsKey, get, put of HashMap. The third is not imuutable and can be changed later, but if you change the key value after putting it, it may not be found. If you change this to DEF after putting it in ABC, it is natural that you can not find it in ABC, but there is a possibility that it will not be found in DEF. The reason is that the hashCode changes, but what originally goes into the place that corresponds to the hashCode of DEF has entered the place that corresponds to the hashCode of ABC.
Equals and hashCode are incorrect for arrays.
Main.java
public static void main(String[] args) {
byte[] b01 = { 1, 2, 3 };
byte[] b02 = { 1, 2, 3 };
int h01 = b01.hashCode();
int h02 = b02.hashCode();
System.out.println(b01 == b02);
System.out.println(b01.equals(b02));
System.out.println(h01 == h02);
int h11 = Arrays.hashCode(b01);
int h12 = Arrays.hashCode(b02);
System.out.println(Arrays.equals(b01, b02));
System.out.println(h11 == h12);
}
Result is···
false
false
false
true
true
The reason why Arrays.equals and Arrays.hashCode are bothered is that the equals and hashCode of the array are incorrect.
How to implement equals and hashCode by wrapping byte [].
ByteArrayWrapper.java
import java.util.Arrays;
public class ByteArrayWrapper {
private byte[] data;
public ByteArrayWrapper(byte[] data) {
this.data = data;
}
public boolean equals(Object other) {
if (other instanceof ByteArrayWrapper) {
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
}
return false;
}
public int hashCode() {
return Arrays.hashCode(data);
}
}
Let's test it.
Main.java
public static void main(String[] args) {
byte[] b01 = { 1, 2, 3 };
byte[] b02 = { 1, 2, 3 };
ByteArrayWrapper w01 = new ByteArrayWrapper(b01);
ByteArrayWrapper w02 = new ByteArrayWrapper(b02);
Map<ByteArrayWrapper, String> map = new HashMap<>();
map.put(w01, "OK");
int h01 = w01.hashCode();
int h02 = w02.hashCode();
System.out.println(w01 == w02);
System.out.println(w01.equals(w02));
System.out.println(h01 == h02);
System.out.println(map.get(w01));
System.out.println(map.get(w02));
}
Result is···
false
true
true
OK
OK
It's natural because we call Arrays.equals and Arrays.hashCode in it.
(1/17 postscript) If you bring it from Stack Overflow, it's useless. Since it only holds the array as it is, modifying the array passed to the constructor will cause a malfunction. It's a big deal, so let's try it.
Main.java
public static void main(String[] args) {
byte[] b01 = { 1, 2, 3 };
byte[] b02 = { 1, 2, 3 };
byte[] b03 = { 9, 2, 3 };
ByteArrayWrapper w01 = new ByteArrayWrapper(b01);
ByteArrayWrapper w02 = new ByteArrayWrapper(b02);
ByteArrayWrapper w03 = new ByteArrayWrapper(b03);
Map<ByteArrayWrapper, String> map = new HashMap<>();
map.put(w01, "OK");
System.out.println(map.get(w01));
b01[0] = 9;
System.out.println(map.get(w01));
System.out.println(map.get(w02));
System.out.println(map.get(w03));
System.out.println(map);
}
When you run ...
OK
null
null
null
{ByteArrayWrapper@9669=OK}
The data is in the map, but I can't get it with the key. To prevent this, make a copy of the array in the constructor.
ByteArrayWrapper.java
import java.util.Arrays;
public class ByteArrayWrapper {
private byte[] data;
public ByteArrayWrapper(byte[] data) {
this.data = data.clone();
}
public boolean equals(Object other) {
if (other instanceof ByteArrayWrapper) {
return Arrays.equals(data, ((ByteArrayWrapper) other).data);
}
return false;
}
public int hashCode() {
return Arrays.hashCode(data);
}
}
If you run the same test program ...
OK
OK
OK
null
{ByteArrayWrapper@7861=OK}
The arrangement inside is protected.
There is a comment that you can go with java.nio.ByteBuffer without creating a wrapper class. Let's test it.
Main.java
public static void main(String[] args) {
byte[] b01 = { 1, 2, 3 };
byte[] b02 = { 1, 2, 3 };
ByteBuffer w01 = ByteBuffer.wrap(b01);
ByteBuffer w02 = ByteBuffer.wrap(b02);
Map<ByteBuffer, String> map = new HashMap<>();
map.put(w01, "OK");
int h01 = w01.hashCode();
int h02 = w02.hashCode();
System.out.println(w01 == w02);
System.out.println(w01.equals(w02));
System.out.println(h01 == h02);
System.out.println(map.get(w01));
System.out.println(map.get(w02));
}
Result is···
false
true
true
OK
OK
OK at all. This is the best feeling. I like the standard library added from JDK 1.4.
There is a comment that even java.math.BigInteger cannot go because the constructor receives byte []. However, when it is {0, 100}, it will be with {100}, right? There is a comment return. Let's test it.
Main.java
public static void main(String[] args) {
byte[] b01 = { 0, 100 };
byte[] b02 = { 0, 100 };
byte[] b03 = { 100 };
BigInteger w01 = new BigInteger(b01);
BigInteger w02 = new BigInteger(b02);
BigInteger w03 = new BigInteger(b03);
Map<BigInteger, String> map = new HashMap<>();
map.put(w01, "OK");
int h01 = w01.hashCode();
int h02 = w02.hashCode();
int h03 = w03.hashCode();
System.out.println(w01 == w02);
System.out.println(w01 == w03);
System.out.println(w01.equals(w02));
System.out.println(w01.equals(w03));
System.out.println(h01 == h02);
System.out.println(h01 == h03);
System.out.println(map.get(w01));
System.out.println(map.get(w02));
System.out.println(map.get(w03));
}
Result is···
false
false
true
true
true
true
OK
OK
OK
That's too bad. {0, 100} and {100} are together. It's dangerous if you use it without knowing it.
String s01 = new String (b01, "UTF-8"); but it looks like garbled characters. String s02 = Arrays.toString (b01); would be legitimate, but the output string would be "[1, 2, 3]". I don't need parentheses or blanks. Therefore, create a utility method that outputs numerical values, numerical values, .... Convert the number to hexadecimal. Convert the number to 36 base. (Character.MAX_RADIX == 36) Base64 encode.
However, it is inefficient because the size is definitely larger than byte [].
I think ByteBuffer is fine, but if you want to use int [] instead of byte [], there is IntBuffer. Or rather, all primitive types are available. Various somehow Buffer. I did not know that.
Strictly not imuutable, but you wouldn't add it with put after specifying it as a key. (1/17 postscript) Looking at the javadoc and source of ByteBuffer, get () is also useless. This is because the current position moves and equals and hashCode target the current position and beyond. equals and hashCode are accessed by get (int). The following notes are written in ByteBuffer / hashCode.
The buffer hash code is content dependent. Avoid using the buffer as a key for hash maps or other data structures unless it is clear that the contents of the buffer will not change in the future.
But if you don't know how to use it outside, you can rest assured that you can't change it by creating ByteArrayWrapper in the usual way. (1/17 postscript) I'm sorry, the first ByteArray Wrapper I put out was not imuutable. Fixed.
(1/18 postscript) I put it at the end of TreeMap, but it is a wrapper that can be used for both. Modified from the source in HashMap: Changed constructor argument from byte [] to byte ..., added equals (byte [] other), added compareTo ()
ByteArrayWrapper.java
import java.util.Arrays;
public class ByteArrayWrapper implements Comparable<ByteArrayWrapper> {
private byte[] data;
public ByteArrayWrapper(byte... data) {
this.data = data.clone();
}
public boolean equals(Object other) {
if (other instanceof ByteArrayWrapper) {
return equals(((ByteArrayWrapper) other).data);
}
return false;
}
public boolean equals(byte[] other) {
return Arrays.equals(data, other);
}
public int hashCode() {
return Arrays.hashCode(data);
}
public int compareTo(ByteArrayWrapper that) {
int n = Math.min(this.data.length, that.data.length);
for (int i = 0; i < n; i++) {
int cmp = Byte.compare(this.data[i], that.data[i]);
if (cmp != 0)
return cmp;
}
return this.data.length - that.data.length;
}
}
Recommended Posts