213

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.

starball
  • 20,030
  • 7
  • 43
  • 238
  • 6
    "I want to create a Map ... 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. – newacct May 19 '09 at 05:26
  • Don't want to spam with another answer, but worth noting there's now a [documentation example](http://stackoverflow.com/documentation/java/99/arrays/17621/converting-arrays-between-primitives-and-boxed-types#t=201612191100551953842) about this. – Mureinik Dec 19 '16 at 11:03

13 Answers13

260

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.

Sheepy
  • 17,324
  • 4
  • 45
  • 69
  • 3
    That's meanwhile the correct way. If you need the Integer-Array as a list you could write: `List list = IntStream.of(q).boxed().collect(Collectors.toList());` – aw-think Feb 27 '15 at 07:53
  • 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 – YoYo Jun 04 '15 at 16:10
  • 1
    @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. – Sheepy Jun 05 '15 at 05:39
  • 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. – YoYo Jun 05 '15 at 06:27
  • @JoD. Yes it is sad. But as long as the stream is boxed (i.e. not an IntStream), `toArray` will always need a constructor, regardless of the source. – Sheepy Jun 05 '15 at 07:26
  • @JoD. I got compilation error `Object[] cannot be converted to Integer[]`. Both on my pc and on ideone: https://ideone.com/uagsSd – Sheepy Jun 05 '15 at 14:44
  • @Sheepy: I prefer the usage of IntStream, because it is more related to the type of data in that stream. The Arrays class is more to manipulate arrays and not streams (even if some methods create streams). So if a third person is reading your code, he/she exactly knows whats going on. It's more readable to say: Make an IntStream of that int array, box it, and make an array or collect a list. As if you say: Arrays make a stream from that int array (what type of?) and box it (to what?). But all of this is a matter of taste. – aw-think Jun 06 '15 at 08:25
  • I am eating my shoe. I was simply wrong - I thought I tried it out, but obviously didn't. `boxed()`, just returns a typed `Stream`, but still needs a typed array generator for the `toArray` to return the correctly typed Array, otherwise it is going to be `Object[]`. I guess that has it benefits too, plus there is probably no other way. – YoYo Jun 06 '15 at 08:37
  • Thanks for supporting JoD. I start from the parameter type which is an array, and it is uniform as you an handle most arrays with one function. But I just figured that I can add both syntaxes in the answer :p @NwDx – Sheepy Jun 08 '15 at 03:11
  • @NwDx somehow I also instinctively use `IntStream.of`, but I think `Arrays.stream` will be favored by many because it's type neutrality due to the overloading. – YoYo Jun 14 '15 at 18:34
  • @JoD. Full ack. If one don't know the type of data than he/she is not able to create an Array of Integer, as you need the type of data for creation. Only untyped lists with the collectors are possible. Or am I wrong? – aw-think Jun 14 '15 at 20:16
  • We need to think [JEP 169](http://openjdk.java.net/jeps/169) where explicit primitive typed classes will be likely deprecated. – YoYo Jun 14 '15 at 20:30
  • @JoD. I do not see where did JEP 169 mention deprecation of boxed primitive class. Plus it has low priority and progress is non-existent. – Sheepy Jun 15 '15 at 02:14
  • @NwDx Well, compared with `Arrays.stream`, `IntStream.of` and other primitive streams also require you to know the data type. In fact if the data type change, the later just result in more code change. – Sheepy Jun 15 '15 at 02:20
  • @Sheepy part of project [Valhalla](http://openjdk.java.net/projects/valhalla/). The deprecation was just my interpolation of things to come. – YoYo Jun 15 '15 at 02:24
  • Can this be applied to 2D array? Can `int[][] data` converted to `Integer[][] whatever`? – Globox Nov 12 '17 at 23:13
  • @Chetan It is possible to apply to multiple dimensions, but I strongly advise against it (better use proper loop), because this approach doesn't scale at all: `Integer[][] newData = Arrays.stream( data ).map( row -> Arrays.stream( row ).boxed().toArray( Integer[]::new ) ).toArray( Integer[][]::new )` - – Sheepy Nov 16 '17 at 08:21
  • What is this new syntax: `Integer[]::new`? – Vijay Chavda Mar 24 '18 at 06:25
  • 1
    @VijayChavda The new syntax, called [method references](https://docs.oracle.com/javase/tutorial/java/javaOO/methodreferences.html), was introduced in 2014 with Java 8 as part of lambda ([JSR 335 Part C](http://cr.openjdk.java.net/~dlsmith/jsr335-0.6.1/C.html)). It means `the "new" method (constructor) of the Integer[] class`. – Sheepy Mar 26 '18 at 05:52
83

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);
Rainer Blome
  • 561
  • 5
  • 15
Eddie
  • 53,828
  • 22
  • 125
  • 145
  • 77
    thats a nasty way to do a for loop. – James Becwar Jul 13 '12 at 20:17
  • 10
    This is a perfectly normal for each... I don't see the nasty part. – Dahaka Aug 03 '12 at 12:33
  • 18
    @Dahaka, the nastiness is in using an iterator of `value` while the indexing variable `i` is there. – lcn Aug 24 '13 at 02:52
  • 1
    @Icn, I can see why some people wouldn't prefer that style, but what makes it so undesirable as to be nasty? – Eddie Sep 23 '13 at 15:28
  • 1
    With Guava I would prefer `List intList = Ints.asList(oldArray);` – eldur Apr 12 '14 at 09:54
  • 16
    @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. – daiscog Jan 09 '15 at 14:46
  • I saw almost same nastiness in our professor's solution to a homework, it was in C, 17 years ago. My experience showed me almost never trying to write some code shorter really helps anymore. – mcvkr Mar 17 '21 at 17:43
  • want to see something as nasty : https://stackoverflow.com/questions/1642028/what-is-the-operator-in-c-c?rq=1 – mcvkr Mar 18 '21 at 14:15
20

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]);
zemiak
  • 361
  • 2
  • 5
11

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
}
k_rollo
  • 5,304
  • 16
  • 63
  • 95
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).

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • 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 May 19 '09 at 01:35
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;
     }
    
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Harsh
  • 2,893
  • 29
  • 16
  • 4
    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]` :) – Eel Lee Mar 09 '18 at 14:52
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);
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mihai Toader
  • 12,041
  • 1
  • 29
  • 33
  • 1
    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 May 19 '09 at 01:58
  • 1
    Oh and throw an NPE from the constructor (probably calling clone) for null data, and not have the check in hashCode. – Tom Hawtin - tackline May 19 '09 at 02:00
  • True .. But the code for equals, hashCode is generated by IDEA :-). It works correctly. – Mihai Toader May 19 '09 at 14:01
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);
    }
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Chris
  • 2,447
  • 1
  • 21
  • 27
2

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.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Thomas Ahle
  • 30,774
  • 21
  • 92
  • 114
  • 2
    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. – Zero3 Jul 31 '14 at 10:44
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 }));
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Matt
  • 11,523
  • 2
  • 23
  • 33
  • 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](https://docs.oracle.com/javase/8/docs/api/?java/nio/IntBuffer.html)… You can use it to [wrap an array](https://docs.oracle.com/javase/8/docs/api/java/nio/IntBuffer.html#wrap-int:A-) and not only has it `hashCode` and `equals`, it’s even comparable. – Holger Jul 21 '16 at 16:31
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);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 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? – forkdbloke Nov 10 '19 at 05:06
  • 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](https://stackoverflow.com/help/promotion): *"...always explain why the solution you're presenting is appropriate and how it works"*. Please respond by [editing (changing) your answer](https://stackoverflow.com/posts/37775001/edit), not here in comments (***without*** "Edit:", "Update:", or similar - the answer should appear as if it was written today). – Peter Mortensen Mar 09 '22 at 12:57
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)
Angelin Nadar
  • 8,944
  • 10
  • 43
  • 53
-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 :-).

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mihai Toader
  • 12,041
  • 1
  • 29
  • 33
  • 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? –  May 19 '09 at 01:34
  • 1
    (Hint, (new int[0]).equals(new int[0]) is false; (new ArrayList()).equals(new ArrayList()) is true. – Tom Hawtin - tackline May 19 '09 at 01:36
  • 1
    Bad idea, as array comparison is based on *reference* equality. That's why Arrays.equals() exists... – sleske May 19 '09 at 01:37
  • Damn :-). I was hoping i was right. The declaration is correct but the semantics is wrong because of the reference equality thing. – Mihai Toader May 19 '09 at 01:44