2

This function converts a 2-dimensional Collection of Strings to a 2-dimensional array of Strings:

public static String[][] toArray(
                             Collection<? extends Collection<String>> values){

    String[][] result = new String[ values.size() ][];
    int i=0;
    for( Collection<String> row : values ){
        result[i] = row.toArray( new String[ row.size() ] );
        i++;
    }
    return result;
}

How would this function be written in order to do this generically:

public static <X> X[][] ArrayArray(Collection<? extends Collection<X>> values){
    // ?
}
Scheintod
  • 7,352
  • 7
  • 35
  • 58

3 Answers3

2

Generics and arrays don't mix. One alternative is to pass a Class instance representing X and use that to create the return array (see Array.newInstance()). I believe a better approach would be to pass an X[][] in as another argument, fill it, and return it:

public static <X> X[][] toArray(Collection<? extends Collection<X>> values,
        X[][] result) {

    int i = 0;
    for (Collection<X> row : values)
        result[i] = row.toArray(result[i++]);

    return result;
}

For example:

List<List<Integer>> l = Arrays.asList(Arrays.asList(1,2,3), 
                                      Arrays.asList(4,5),
                                      Arrays.asList(6));

System.out.println(Arrays.deepToString(toArray(l, new Integer[l.size()][0])));
[[1, 2, 3], [4, 5], [6]]
arshajii
  • 118,519
  • 22
  • 219
  • 270
  • Hi, thanks. Your missing "result[i] =" in your code. And in the example you don't need to specify the second dimension: "new Integer[2][]" is good. But this solution misses (at least my) point of having this function. I don't want to create the array in advance. – Scheintod Aug 07 '13 at 16:07
  • @Scheintod You do need the second dimension, and you don't need `result[i] =` if the array is appropriately sized. If you don't want to create the array you can 1) return an `Object[][]` or 2) use the `Class` approach that I described. – arshajii Aug 07 '13 at 16:10
  • ah. sorry. missed the (result[i++]) part because I haven't used it this way. In my opinion this cries "hard to find errors". Because if you specify it to small you won't get any warnings but no result. – Scheintod Aug 07 '13 at 16:16
  • @Scheintod You can easily add a check for that, `result.length == values.size()`. The idea remains the same. – arshajii Aug 07 '13 at 16:18
  • Ah. Difficult to follow the edits :) Thank you. I will mark this as correct. For me mainly because of the reference to Arrays.newInstance with which I can figure it out for myself. But for reference this would be the more "interessting" solution (hint ;)). – Scheintod Aug 07 '13 at 16:26
2

I'm afraid you can't.

It's because Java's arrays (unlike generics) contain, at runtime, information about its component type. So you must know the component type when you create the array. Since you don't know what T is at runtime, you can't create the array

Source: https://stackoverflow.com/a/2931240, by newacct

Community
  • 1
  • 1
delucas
  • 51
  • 6
  • Technically this is the most correct answer to my question. But it's the least helpful. – Scheintod Aug 07 '13 at 16:53
  • (: true... but you can't respect the interface you proposed and solve your problem. I thought you needed to honor it, that's why I didn't post any alternatives. – delucas Aug 07 '13 at 18:06
0

So it's not possible because of type erasure. But there is at least a solution for:

public static <X> X[][] toArray( Class<X> xClass, 
        Collection<? extends Collection<X>> values ){

    @SuppressWarnings( "unchecked" )
    X[][] result = (X[][])Array.newInstance( xClass , values.size(), 0 );

    int i=0;
    for( Collection<X> row : values ){

        @SuppressWarnings( "unchecked" )
        X[] rowAsArray = (X[])Array.newInstance( xClass, row.size() );
        result[i] = row.toArray( rowAsArray );
        i++;
    }
    return result;
}

(thanks for pointing me to Array.newInstance)

Scheintod
  • 7,352
  • 7
  • 35
  • 58