Reputation:
The put function works fine but the get function does not. Apparently I don't know the trick.
>> X = [ 1, 2, 3];
>> M = java.util.HashMap;
>> M.put(X,1);
>> M.get([1,2,3])
ans = []
I have searched and read many posts but could not find a solution to this problem. It would be great if someone can let me know the trick.
Upvotes: 10
Views: 3891
Reputation: 125874
I don't think you can use numeric vectors or matrices as keys in a Java hashmap. You would instead have to convert the vector or matrix into a single unique key, such as a unique character string representation of the values in the vector or matrix. There are a few ways to do this:
For integer arrays, you can use the CHAR function to convert the integers to their equivalent ASCII representations, thus creating a character string. This will only work effectively for integer values between 0 and 65535, since anything outside this range is likely to have undefined behavior. Here's an example:
X = [1 2 3; 4 5 6]; % X is a 2-by-3 matrix
keyValue = char(X(:)'); % Reshape X to a row vector and convert to ASCII
For integer values too large to use CHAR, you can use INT2STR instead:
keyValue = int2str(X(:)');
For floating-point arrays, you can use the NUM2STR function to create a formatted string representation of each of the array elements concatenated together. Here's an example:
X = rand(2,3)*9999; % X is a 2-by-3 matrix of random double values
keyValue = num2str(X(:)','%10.5f');
To ensure uniqueness of the key (by avoiding round-off of the floating-point value) you could instead convert the double values to their complete 64-bit binary representations using DEC2BIN. However, this will likely result in huge character keys:
keyValue = reshape(dec2bin(X(:),64)',1,[]);
One drawback of these options is that your keys could potentially end up being rather long character strings. I'm not sure if there is an upper limit on the number of characters in the key or if there is a performance hit in using long character strings for keys.
Upvotes: 1
Reputation: 1999
Matlab structs provide very fast lookup from alphanumeric keys (well, [a-zA-Z][a-zA-Z_0-9]* matching); failing that, if you are trying to hash from numbers, I would suggest using sparse arrays with array doubling; let the arrayvalue point to the index into whatever you are trying to look up. hth
Upvotes: 0
Reputation: 23898
I think the problem is that Java primitive arrays don't provide the right equals() and hashCode() for you. They use the standard Object methods that compare by object identity instead of contained values. When using nonscalar arrays as keys in a HashMap, Matlab will convert them to double[], but they'll be distinct Java objecs, so they'll get this behavior.
If you wrapped your array values in a Java object that provided by-value behavior for equals() and hashCode() before using them as keys, this could work. Luckily, java.util.Arrays provides by-value implementations for the primitive arrays. We just need to slap them in a wrapper class that provides the interface that HashMap is expecting.
package test;
import java.util.Arrays;
/**
* A double[] that with by-value semantics for equals() and hashCode() so you
* can use it in HashMaps.
* In a non-toy class, you'd probably use switch statements to support arrays
* of any primitive type. In a language with real generics, you'd just template
* this.
*/
public class EqualByValueDoubleArray {
private double[] x;
public EqualByValueDoubleArray(double[] x) { this.x = x; }
public double[] getArray() { return x; };
public boolean equals(Object obj) {
if (obj instanceof EqualByValueDoubleArray) {
return Arrays.equals(this.x, ((EqualByValueDoubleArray)obj).x);
} else {
return false;
}
}
public int hashCode() { return Arrays.hashCode(x); }
}
Now you can wrap them and use them as keys from Matlab.
function scratch_array_keyed_hashmap
import test.EqualByValueDoubleArray;
map = java.util.HashMap;
a = [1 2 3 4 5]';
key = EqualByValueDoubleArray(a);
map.put(key, 'my value');
% Separate key so we know it's comparing by value, not Java object identity
key2 = EqualByValueDoubleArray(a);
gotBack = map.get(key2)
This works under R2008b for me.
>> scratch_array_keyed_hashmap
gotBack =
my value
For easier use, you could create a HashMap subclass that checked the type of its input keys, and automatically wrapped primitive arrays in this by-value wrapper.
Upvotes: 6
Reputation: 23955
If you're using a more recent version of MATLAB (2008b or later, I think) then MATLAB has its own map class which works for certain kinds of keys. See the documentation: containers.Map
Upvotes: -1