Java: Array of primitive data types does not autobox
Asked Answered
K

10

31

I have a method like this:

public static <T> boolean isMemberOf(T item, T[] set)
{
    for (T t : set) {
        if (t.equals(item)) {
            return true;
        }
    }
    return false;
}

Now I try to call this method using a char for T:

char ch = 'a';
char[] chars = new char[] { 'a', 'b', 'c' };
boolean member = isMemberOf(ch, chars);

This doesn't work. I would expect the char and char[] to get autoboxed to Character and Character[], but that doesn't seem to happen.

Any insights?

Kyongkyoto answered 5/2, 2009 at 20:24 Comment(0)
T
46

There is no autoboxing for arrays, only for primitives. I believe this is your problem.

Telmatelo answered 5/2, 2009 at 20:28 Comment(5)
Yes, that's the problem. That seems so broken to me... or at least, counter-intuitive.Kyongkyoto
And a limitation of Java, IMHO.Camporee
I personally don't like autoboxing, as it's a comparatively expensive operation I'd like to be able to prevent unless truly necessary.Telmatelo
As long as you are within the object cache, the cost autoboxing and unboxing is notional. Auto-boxing is never necessary however.Camporee
If you're not gonna have autoboxing at least you should allow for creating Lists with primitive types, this just makes turning a char[] into a List<Character> way more difficult than it should.Manmade
V
13

Why would char[] be boxed to Character[]? Arrays are always reference types, so no boxing is required.

Furthermore, it would be hideously expensive - it would involve creating a new array and then boxing each char in turn. Yikes!

Varicocele answered 5/2, 2009 at 20:30 Comment(1)
From a technical perspective, I totally agree with you. From a user-centered perspective, it seems like this should "just work". (I suppose this wouldn't be a problem at all if it weren't for the use of primitive types in the first place)Kyongkyoto
A
3

You could use reflection to get a method that works for all types of arrays, but you would lose type safety, so this is probably not what you want.

import java.lang.reflect.Array
public static boolean isMemberOfArray(Object item, Object array)
{
    int n = Array.getLength(array)
    for (int i = 0; i < n; i++) {
        if (Array.get(array, i).equals(item)) {
            return true;
        }
    }
    return false;
}
Aude answered 2/5, 2009 at 22:57 Comment(0)
I
2

Correct, there is no autoboxing for arrays (which results in weirdness in cases like int[] ints; ...; Arrays.asList(ints) - asList returns a List containing a single Object, the array!)

Here's a simple utility to box an array.

public static Integer[] boxedArray(int[] array) {
    Integer[] result = new Integer[array.length];
    for (int i = 0; i < array.length; i++)
        result[i] = array[i];
    return result;
}

You will need a different version for each primitive type, of course.

Ingenuity answered 13/2, 2009 at 8:19 Comment(0)
B
1

This appears to be by design, both to avoid such an expensive autoboxing operation, and because generics have to be backwards-compatible with the existing Java bytecode.

See this article and this bug, for example.

Brisk answered 5/2, 2009 at 20:33 Comment(0)
S
1

Arrays are a low-level implementation type of thing. char[] will be a contiguous area of memory with two-byte chars. Character[] will be a contiguous area of memory with four or eight-byte references. You cannot get a Character[] to wrap a char[]. However a List<Character> could wrap a char[].

Arrays of references are not usually a good idea unless you are writing low-level code. You could, if you wish, write or obtain an equivalent of java.util.Arrays.asList.

Snippy answered 5/2, 2009 at 20:34 Comment(0)
J
1

As others have mentioned, there is no autoboxing for arrays of primitives. If you want to use your method with primitive arrays, you will need to provide an overload for each primitive type. This seems to be the standard way of doing things in the class libraries. See the overloads in java.util.Arrays, for example.

Journal answered 2/8, 2009 at 7:54 Comment(0)
L
1

First, I would try to avoid arrays as much as you can, use lists instead.

There is no autoboxing for arrays, but there is autoboxing for varargs. So if you declare your method as (with the same body):

public static <T> boolean isMemberOf(T item, T ... set)

then you can write

isMemberOf('a', 'a', 'b', 'c');

Personally, I prefer using google's guava, where you can write things like

char ch = 'a';
char[] chars = new char[] { 'a', 'b', 'c' };
boolean member = isMemberOf(ch, Chars.asList(chars).toArray(new Character[0]));

Your code was probably just an example, but if you really wanted to test membership, in you can do it like this:

Chars.contains(chars, ch);

or

ImmutableSet.of('a', 'b', 'c').contains('a')
Langtry answered 14/11, 2012 at 10:21 Comment(0)
F
1

Enter Java 8 and let primArray be an identifier of type PrimType[], then you can do the following:
BoxedType[] boxedArray = IntStream.range(0, primArray.length).mapToObj(i -> primArray[i]).toArray(BoxedType[] :: new);

Froufrou answered 1/4, 2016 at 17:50 Comment(0)
C
0

A simpler way to do this is

char ch = 'a';
String chars = "abc";
boolean member = chars.indexOf(ch) >= 0;
Camporee answered 5/2, 2009 at 22:56 Comment(1)
True, but I was hoping to create a method that I could use with any type.Kyongkyoto

© 2022 - 2024 — McMap. All rights reserved.