user165466
user165466

Reputation:

How can I use MATLAB arrays as keys to the HashMap java objects?

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

Answers (4)

gnovice
gnovice

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

shabbychef
shabbychef

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

Andrew Janke
Andrew Janke

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

kenm
kenm

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

Related Questions