-2

I have this method:

//not related to the error, but included for reference
ArrayList<ArrayList<Color>> a = new ArrayList<ArrayList<Color>>(); 

void addColorToList(float[] j) //this array always length 3
{
    float[] k = Arrays.copyOf(j, 3);
    Arrays.sort(k);
            //Error in the following line
    a.get(Arrays.asList(j).indexOf(k[0])).add(new Color(j[0], j[1], j[2])); 
}

and this error:

Exception in thread "AWT-EventQueue-1" 
        java.lang.ArrayIndexOutOfBoundsException: -1

I've determined that my code always calls a.get() with -1, because Arrays.asList(j). indexOf(k[0]) does not find the element. However, I cannot figure out why this doesn't work as I would expect it to. I tried printing out the result of Arrays.asList(j), but I'm not really sure what to make of the result: [[F@307af497]. Can anybody tell me what the issue is?

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
Azar
  • 1,086
  • 12
  • 27
  • @OliCharlesworth No. `j` is generated with `addColorToList((new Color(r.nextInt())).getRGBColorComponents(new float[3]));`, `r` being a `Random` object. Also, I've printed `j`, and there's nothing out of the ordinary about it. – Azar Feb 16 '14 at 01:41

1 Answers1

3

Lets start with this:

I tried printing out the result of Arrays.asList(j), but I'm not really sure what to make of the result: [[F@307af497].

You are printing a list using (effectively) the toString() method. So ...

  • The outer '[' and ']' are from the list formatting.

  • You have a list consisting of one element.

  • That element is an array of float. (The [F@307af497 is produced by Object.toString(), and [F is how the type float[] is rendered ...)


This is actually an important clue. Arrays.asList(float[]) is returning a "list of float[]" ...

But why?

Well, because that's what it is supposed to do!!

The Arrays.asList signature is Arrays.asList<T>(T ...). That means it expects either an explicit T[] ... or a sequence of zero or more T instances, which it will then wrap as an Object[]. The Object[] is then wrapped as a List<Object> (roughly speaking).

The critical thing here is that the actual type T must be a reference type ... not a primitive type.

But your code seems to be expecting an overloaded method like this Arrays.asList(float ...) ... and expecting that that will give you your float[] wrapped as a List<Float>.

Unfortunately, there is no such overload for asList.

So what is actually happening is that:

  • your call is binding to Arrays.asList<float[]>(float[] ...)

  • the varargs is causing j to be wrapped in an array; i.e. equivalent to new float[][]{j}

  • the result is an instance of List<float[]> ... with one element.


So what is the solution?

In general ...

  • One solution would be to represent your floats as a Float[] rather than a float[]. Ideally, you would push this change back through the code that created the array in the first place, etcetera.

  • A second solution would be to convert the float[] to a Float[] before calling asList. The simple way to do that is with a simple loop. (There may also be a 3rd-party library for doing this.) The downsides are:

    • the conversion needs to happen each time you call this method which could be expensive if you call it a lot, and

    • there is no connection between the original array and the array that you have wrapped ... if you wanted to update the array through the list wrapper.

But in this case, the best solution is to simply replace this:

    Arrays.asList(j).indexOf(k[0])

with a simple loop that iterates over the original float[] testing for an entry that matches k[0].


The moral of this story: you can easily shoot yourself in the foot by striving for an elegant solution in Java.

Often, dumb code is better. (Both faster, and more correct.)

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
  • "That element is an array of float." Is that what _should_ happen when using that method? I've used it before, and was under the impression that it should return `List`, not `List`. – Azar Feb 16 '14 at 01:48
  • @Azar Check the asList method signature. It recieves a variable amount of types T and returns a List. You are passing 1 argument of type float[], so it is returning a list of arrays. – xp500 Feb 16 '14 at 01:50
  • @xp500 What you're saying seems to be true. But if that's the case, then how does `java.util.Arrays.asList(theArray).indexOf(o)` from [this question](http://stackoverflow.com/questions/4962361/where-is-javas-array-indexof?lq=1) work? – Azar Feb 16 '14 at 01:55
  • @Azar By reading Steven's edit I suggest you use an array of type Float instead of primitive float. That should work correctly. – xp500 Feb 16 '14 at 02:02
  • @xp500 Yes, I came to that conclusion as well. – Azar Feb 16 '14 at 02:04
  • @Azar - Actually, that is not the correct conclusion. See the final form of my answer. – Stephen C Feb 16 '14 at 04:07
  • @StephenC Perhaps I should have mentioned this, but I'm golfing against a friend, and this was part of my code. Therefore, although you can (and I did) "shoot yourself in the foot" with an elegant solution, using `Float[]` worked, and cut out a good many chars. Elegance over efficiency (and readability), in this case. – Azar Feb 16 '14 at 04:12