views:

1212

answers:

5

I'm new to Java and very confused.

I have a large dataset of length 4 int[] and I want to count the number of times that each particular combination of 4 integers occurs. This is very similar to counting word frequencies in a document.

I want to create a Map<int[], double> that maps each int[] to a running count as the list is iterated over, but Map doesn't take primitive types.

so I made Map<Integer[], Double>

my data is stored as an ArrayList<int[]> so my loop should be something like

ArrayList<int[]> data = ... // load a dataset`

Map<Integer[], Double> frequencies = new HashMap<Integer[], Double>();

for(int[] q : data) {

    // **DO SOMETHING TO convert q from int[] to Integer[] so I can put it in the map

    if(frequencies.containsKey(q)) {
    frequencies.put(q, tfs.get(q) + p);
    } else {
        frequencies.put(q, p);
    }
}

I'm not sure what code I need at the comment to make this work to convert an int[] to an Integer[]. Or maybe I'm fundamentally confused about the right way to do this.

A: 

you don't need. int[] is an object and can be used as a key inside a map.

Map<int[], Double> frequencies = new HashMap<int[], Double>();

is the proper definition of the frequencies map.

This was wrong :-). The proper solution is posted too :-).

Toader Mihai Claudiu
<int [], Double>?
ninesided
Many thanks for your replyI tried this first and it compiled fine, but it seems like `frequencies.containsKey(q)` would always be false even if I've `put` the same array twice - is there some glitch here involving java's definition of equality with int[]'s?
Map<List<Integer>,Integer> would work better.
Tom Hawtin - tackline
(Hint, (new int[0]).equals(new int[0]) is false; (new ArrayList<Integer>()).equals(new ArrayList<String>()) is true.
Tom Hawtin - tackline
Bad idea, as array comparison is based on *reference* equality. That's why Arrays.equals() exists...
sleske
Damn :-). I was hoping i was right. The declaration is correct but the semantics is wrong because of the reference equality thing.
Toader Mihai Claudiu
+2  A: 

If you want to convert an int[] to an Integer[], there isn't an automated way to do it in the JDK. However, you can do something like this:

int[] oldArray;

... // Here you assign and fill oldArray

Integer[] newArray = new Integer[oldArray.length];
int i = 0;
for (int value : oldArray) {
    newArray[i++] = Integer.valueOf(value);
}

If you have access to the Apache lang library, then you can use the ArrayUtils.toObject(int[]) method like this:

Integer[] newArray = ArrayUtils.toObject(oldArray);
Eddie
+3  A: 

Presumably you want the key to the map to match on the value of the elements instead of the identity of the array. In that case you want some kind of object that defines equals and hashCode as you would expect. Easiest is to convert to a List<Integer>, either an ArrayList or better use Arrays.asList. Better than that you can introduce a class that represents the data (similar to java.awt.Rectangle but I recommend making the variables private final, and the class final too).

Tom Hawtin - tackline
Oh, here's an array conversion question, just the other way around: http://stackoverflow.com/questions/564392/converting-an-array-of-objects-to-an-array-of-their-primitive-types
Tom Hawtin - tackline
+4  A: 

I was wrong in a previous answer. The proper solution is to use this class as a key in the map wrapping the actual int[].

public class IntArrayWrapper {
        int[] data;

        public IntArrayWrapper(int[] data) {
            this.data = data;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            IntArrayWrapper that = (IntArrayWrapper) o;

            if (!Arrays.equals(data, that.data)) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return data != null ? Arrays.hashCode(data) : 0;
        }
    }

and change your code like this:

   Map<IntArrayWrapper, Double > freqs = new HashMap<IntArrayWrapper, Double>();

    for (int[] data : datas) {
        IntArrayWrapper wrapper = new IntArrayWrapper(data);

        if ( freqs.containsKey(wrapper)) {
            freqs.put(wrapper, freqs.get(wrapper) + p);
        }

        freqs.put(wrapper, p);
    }
Toader Mihai Claudiu
I'd make the class final, and the field private final. Make a defensive copy of the array in the constructor (clone). And just return the value from Arrays.equals rather than have that peculiar if statement. toString would be nice.
Tom Hawtin - tackline
Oh and throw an NPE from the constructor (probably calling clone) for null data, and not have the check in hashCode.
Tom Hawtin - tackline
True .. But the code for equals, hashCode is generated by IDEA :-). It works correctly.
Toader Mihai Claudiu
+2  A: 

Rather than write your own code you can use an IntBuffer to wrap the existing int[] without having to copy the data into an Integer array

int[] a = {1,2,3,4};
IntBuffer b = IntBuffer.wrap(a);

IntBuffer implements comparable so you are able to use the code you already have written. Formally maps compare keys such that a.equals(b) is used to say two keys are equal, so two IntBuffers with array 1,2,3 - even if the arrays are in different memory locations - are said to be equal and so will work for your frequency code.

ArrayList<int[]> data = ... // load a dataset`

Map<IntBuffer, Double> frequencies = new HashMap<IntBuffer, Double>();

for(int[] a : data) {

    IntBuffer q = IntBuffer.wrap(a);

    if(frequencies.containsKey(q)) {
        frequencies.put(q, tfs.get(q) + p);
    } else {
        frequencies.put(q, p);
    }

}

Hope that helps

Chris