222

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.

2
  • 6
    "I want to create a Map<int[], double> ... but Map doesn't take primitive types." As one of the posts below pointed out, int[] is not a primitive type, so that is not the real problem. The real problem is that arrays do not override .equals() to compare the elements. In that sense converting to Integer[] (as your title says) does not help you. In the code above, frequencies.containsKey(q) would still not work as you expect because it uses .equals() to compare. The real solution is to not use arrays here. Commented May 19, 2009 at 5:26
  • Don't want to spam with another answer, but worth noting there's now a documentation example about this. Commented Dec 19, 2016 at 11:03

14 Answers 14

272

Native Java 8 (one line)

With Java 8, int[] can be converted to Integer[] easily:

int[] data = {1,2,3,4,5,6,7,8,9,10};

// To boxed array
Integer[] what = Arrays.stream( data ).boxed().toArray( Integer[]::new );
Integer[] ever = IntStream.of( data ).boxed().toArray( Integer[]::new );

// To boxed list
List<Integer> you  = Arrays.stream( data ).boxed().collect( Collectors.toList() );
List<Integer> like = IntStream.of( data ).boxed().collect( Collectors.toList() );

As others stated, Integer[] is usually not a good map key. But as far as conversion goes, we now have a relatively clean and native code.

Sign up to request clarification or add additional context in comments.

21 Comments

That's meanwhile the correct way. If you need the Integer-Array as a list you could write: List<Integer> list = IntStream.of(q).boxed().collect(Collectors.toList());
to convert to an Integer[] I would actually suggest using following syntax: Integer[] boxed = IntStream.of(unboxed).boxed().toArray(); In the similar fashion as @NwDx
@JoD. Yes, that works. But IntStream.of calls Arrays.stream anyway. I think it comes down to personal preference - I prefer one overridden function, some love using more explicit class.
Fix the signature of toArray in your example, and then we are in agreement. As a matter of fact, I might prefer your version instead.
@VijayChavda The new syntax, called method references, was introduced in 2014 with Java 8 as part of lambda (JSR 335 Part C). It means the "new" method (constructor) of the Integer[] class.
|
85

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 would 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);

8 Comments

thats a nasty way to do a for loop.
This is a perfectly normal for each... I don't see the nasty part.
@Dahaka, the nastiness is in using an iterator of value while the indexing variable i is there.
@Icn, I can see why some people wouldn't prefer that style, but what makes it so undesirable as to be nasty?
@icn, @Dahaka: The nastiness is that since you're already using an incrementing index variable, there's no need to use a for-each, too; although it looks pretty and simple in the code, behind the scenes that involves unnecessary overhead. A traditional for (int i...) loop would be more efficient here.
|
23

Convert int[] to Integer[]:

    import java.util.Arrays;
    ...

    int[] aint = {1,2,3,4,5,6,7,8,9,10};
    Integer[] aInt = new Integer[aint.length];

    Arrays.setAll(aInt, i -> aint[i]);

Comments

12

Using regular for-loop without external libraries:

Convert int[] to Integer[]:

int[] primitiveArray = {1, 2, 3, 4, 5};
Integer[] objectArray = new Integer[primitiveArray.length];

for(int ctr = 0; ctr < primitiveArray.length; ctr++) {
    objectArray[ctr] = Integer.valueOf(primitiveArray[ctr]); // returns Integer value
}

Convert Integer[] to int[]:

Integer[] objectArray = {1, 2, 3, 4, 5};
int[] primitiveArray = new int[objectArray.length];

for(int ctr = 0; ctr < objectArray.length; ctr++) {
    primitiveArray[ctr] = objectArray[ctr].intValue(); // returns int value
}

Comments

8

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).

1 Comment

Oh, here's an array conversion question, just the other way around: stackoverflow.com/questions/564392/…
7

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);
}

3 Comments

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.
Oh and throw an NPE from the constructor (probably calling clone) for null data, and not have the check in hashCode.
True .. But the code for equals, hashCode is generated by IDEA :-). It works correctly.
7
  1. Convert int[] to Integer[]

     public static Integer[] toConvertInteger(int[] ids) {
    
         Integer[] newArray = new Integer[ids.length];
         for (int i = 0; i < ids.length; i++) {
             newArray[i] = Integer.valueOf(ids[i]);
         }
         return newArray;
     }
    
  2. Convert Integer[] to int[]

     public static int[] toint(Integer[] WrapperArray) {
    
        int[] newArray = new int[WrapperArray.length];
        for (int i = 0; i < WrapperArray.length; i++) {
            newArray[i] = WrapperArray[i].intValue();
        }
        return newArray;
     }
    

1 Comment

This is rather inaccurate due to autoboxing. In 1st example you can simply do newArray[i] = ids[i]; and in 2nd one newArray[i] = WrapperArray[i] :)
5

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);
    }
}

Comments

2

This worked like a charm!

int[] mInt = new int[10];
Integer[] mInteger = new Integer[mInt.length];

List<Integer> wrapper = new AbstractList<Integer>() {
    @Override
    public int size() {
        return mInt.length;
    }

    @Override
    public Integer get(int i) {
        return mInt[i];
    }
};

wrapper.toArray(mInteger);

2 Comments

Hi Tunaki, can you please explaing the above code for me? when i run through debugger, i see that get and size method gets called automatically, i.e with out them being called explicitely !! how so?! can you please explain?
Even if it worked like a charm, an explanation would be in order. E.g., what is the idea/gist? On what system (incl. versions) and with what version of Java was it tested on? From the Help Center: "...always explain why the solution you're presenting is appropriate and how it works". Please respond by editing (changing) your answer, not here in comments (without "Edit:", "Update:", or similar - the answer should appear as if it was written today).
2

I am not sure why you need a Double in your map. In terms of what you're trying to do, you have an int[] and you just want counts of how many times each sequence occurs(?). Why would this require a Double anyway?

I would create a wrapper for the int array with a proper .equals and .hashCode methods to account for the fact that int[] object itself doesn't consider the data in its version of these methods.

public class IntArrayWrapper {
    private int values[];

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

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + Arrays.hashCode(values);
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        IntArrayWrapper other = (IntArrayWrapper) obj;
        if (!Arrays.equals(values, other.values))
            return false;
        return true;
    }

}

And then use Google Guava's multiset, which is meant exactly for the purpose of counting occurrences, as long as the element type you put in it has proper .equals and .hashCode methods.

List<int[]> list = ...;
HashMultiset<IntArrayWrapper> multiset = HashMultiset.create();
for (int values[] : list) {
    multiset.add(new IntArrayWrapper(values));
}

Then, to get the count for any particular combination:

int cnt = multiset.count(new IntArrayWrapper(new int[] { 0, 1, 2, 3 }));

1 Comment

This IntArrayWrapper definitely is the right approach for using an int[] array as hash key, but it should be mentioned that such a type already exists… You can use it to wrap an array and not only has it hashCode and equals, it’s even comparable.
1

Int is a primitive. Primitives can’t accept null and have default value. Hence, to accept Null you need to use wrapper class Integer.

Option 1:

int[] nos = { 1, 2, 3, 4, 5 };
Integer[] nosWrapped = Arrays.stream(nos) 
                             .boxed()
                             .toArray(Integer[]::new);
nosWrapped[5] = null // can store null

Option 2: You can use any data structure that use wrapper class Integer

int[] nos = { 1, 2, 3, 4, 5 };
List<Integer> = Arrays.asList(nos)

1 Comment

Option 2 will cause incompatible types error due to int[] not being same as Integer[]
1

the easiest way to do it:

int[] arrayOfInts = {1 , 3 , 5 , 6};
    Integer[] arrayOfObjects = new Integer[arrayOfInts.length];
    Arrays.setAll(arrayOfInts,
            i -> arrayOfObjects[i] = arrayOfInts[i]
            );

and if you want to put it in a List:

int[] arrayOfInts = {1 , 3 , 5 , 6};
    List<Integer> intList = new ArrayList<>(Collections.nCopies(arrayOfInts.length, 0));
    intList.replaceAll(e -> arrayOfInts[intList.indexOf(e)]);

Comments

0

Though the below compiles, it throws a ArrayStoreException at runtime.


Converting an int[], to an Integer[]:

int[] old;
...
Integer[] arr = new Integer[old.length];
System.arraycopy(old, 0, arr, 0, old.length);

I must admit I was a bit surprised that this compiles, given System.arraycopy being lowlevel and everything, but it does. At least in Java 7.

You can convert the other way just as easily.

1 Comment

I don't see much value in keeping this answer "for reference" when it doesn't work. The documentation for System.arraycopy() even states that it won't work for this purpose.
-6

You don't need it. 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 :-).

4 Comments

Many thanks for your reply I 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?
(Hint, (new int[0]).equals(new int[0]) is false; (new ArrayList<Integer>()).equals(new ArrayList<String>()) is true.
Bad idea, as array comparison is based on reference equality. That's why Arrays.equals() exists...
Damn :-). I was hoping i was right. The declaration is correct but the semantics is wrong because of the reference equality thing.