14

I am trying to write a function that takes some strings and does something with them.

The only thing I'm going to do that the set of strings is loop over them. Right now I end up with an awkward construct along the lines of

public void foo(String[] myStrings){
    foo(java.util.Arrays.asList(myStrings));
}

public void foo(Iterable<String> myStrings){
    for(String i : myStrings){
        bar(i);
    }
}

which feels redundant since

for(String i : myStrings){
    bar(i);
}

would be perfectly valid code for myStrings of type String[].

Is there a class that I can have foo accept which will allow both collections and arrays?

BostonJohn
  • 2,631
  • 2
  • 26
  • 48

3 Answers3

13

See Why is an array not assignable to Iterable?

Short answer: no. Array types are synthetic code, as I understand it, and thus do not implement Iterable or any other types. You need to provide an overloaded method or require clients to call Arrays.asList at the call site.

Community
  • 1
  • 1
Thorn G
  • 12,620
  • 2
  • 44
  • 56
3

Unfortunately array does not implement Iterable, despite the for-each loop working on both.

You could exclusively only accept Iterable, as passing around arrays is a bit old school (the performance benefits of arrays over ArrayList is negligible). An existing array can be easily wrapped and converting to a List using Arrays.asList(T... a).

Steve Kuo
  • 61,876
  • 75
  • 195
  • 257
  • That is a good call. I use ArrayLists in any new code that I write, but there are a lot of arrays floating around that I've inherited. – BostonJohn Dec 10 '12 at 19:05
3

Although arrays can be used in a for-each loop, they do not implement Iterable. There are simply two possibilities, either overload the method as you've stated, or just provide only the iterable variant and force the client to call Arrays.asList().

In case you want to also provide the array overload, you might change its signature from simple array to varargs:

public void foo(String... myStrings){
    foo(java.util.Arrays.asList(myStrings));
}

In such case, beware of the incompatibility between primitive arrays and primitive-wrapper arrays:

static void foo(int... ints) {}
foo(new Integer[] {1, 2}); // compile error

and:

static void foo(Integer... integers) {}
foo(new int[] { 1, 2 }); // compile error

And the most obscure part, with generic varargs:

static <T> T[] foo(T... ts) {
    return ts;
}

If you pass an array of Integers:

Integer[] integers = { 1, 2 };
System.out.println(Arrays.deepToString(foo(integers)));
> [1, 2]

The value of ts is an array of Integers with 2 elements: 1 and 2.

However, if you pass an array of primitive ints, a funny thing happens:

int[] ints = { 1, 2 };
System.out.println(Arrays.deepToString(foo(ints)));
> [[1, 2]]

In this case, the value of ts is an array of int arrays (int[][]) with only one element, which is the originally passed array. The reason for this is that an int isn't an Object (autoboxing doesn't help here), while an array of ints is, so the value of the T type parameter becomes int[].

Natix
  • 14,017
  • 7
  • 54
  • 69