How do I refactor this loop?
You can't shouldn't (*) do this in a single method. Item[]
and List<Item>
are unrelated types.
You should make one of the overloads call the other: either something(Item... items)
calls something(List<Item>)
, or something(List<Item>)
calls something(Item... items)
.
Of the two options, it is better for the array overload to call the list overload:
public void something(Item... items) {
something(Arrays.asList(item));
}
This is cheap, because it doesn't copy the array, but rather wraps it: creating the List
is O(1)
.
If you were to invoke the array overload from the list overload:
public void something(List<Item> items) {
something(items.toArray(new Item[0]));
}
This would be more expensive, since the toArray
call has to create and populate an array: it is an O(n)
operation, where n
is the size of the list. However, it has the slight advantage that something
would not be able to replace the contents of the List
, since any updates to the array are simply discarded after execution.
(*) You can, but it would be really gross, and not type-safe, as you'd have to accept an Object
parameter, as there is no other common super type of List<Item>
and Item[]
; and you'd still end up having to repeat the loops for the two types; and you'd have to handle the possibility of a completely unrelated type being passed in (at runtime):
public void something(Object obj) {
if (obj instanceof List) {
for (Object element : (List<?>) obj) {
Item item = (Item) element; // Potential ClassCastException.
doStuff();
}
} else if (obj instanceof Item[]) {
for (Item item : (Item[]) obj) {
doStuff();
}
} else {
throw new IllegalArgumentException();
}
}
What a mess. Thank the maker for overloads.
If you use Java 8 you could also just call forEach
or map
on your Stream
and you are done, e.g.
yourStream.forEach(doStuff());
where doStuff()
is the consumer dealing with the String or use yourStream.forEach(s -> doStuff())
if you don't want to handle the string and just do stuff
.
You can obtain a stream as follows:
Stream.of(yourArray) // or Arrays.stream(yourArray)
.forEach(doStuff());
and for your list:
list.stream()
.forEach(doStuff());
The main benefit of using the streams is probably readability. It may lose regarding performance and it may also lose if you don't want to call Stream.of/Arrays.stream
or Collection.stream()
just to obtain the stream.
If you really want to keep the something(...)
method (being able to deal with both: the varargs and the list) you are still requiring an overloaded method or use Andy Turner's proposal with the Object
-parameter-method.
You can implement a single method, in this case, the second one because it has a list as parameter. Instead of the first method, you can convert the array in a list with Arrays.asList(items)
and then, you can call the first method. So, in the end, you will have only a single method (that has a list as parameter).
Also, if the items list has few elements, you can use the lambda expressions from Java 8:
items.foreach(item -> doStuff(item));
So, you will not have a method that contains only one loop and the code will be easier to read.