Split Collection to two list depend on criteria

Let's say I have a List contains numbers:

1,2,3,4,5,6,7,8,9

and I would like to split this to two lists:

• odd numbers
• even numbers

So I can do this two ways:

Solution 1:

Create two methods:

List<Integer> filterOdd(List<Integer> numbers){
List<Integer> result  = new ArrayList<Integer>();
for(Integer n : numbers){
if(n % 2 != 0){
}
}
return result;
}

List<Integer> filterEven(List<Integer> numbers){
List<Integer> result  = new ArrayList<Integer>();
for(Integer n : numbers){
if(n % 2 == 0){
}
}
return result;
}


and in the code I will call:

List<Integer> numbers = Arrays.asList(new Integer[] { 1,2,3,4,5,6,7,8,9 });

List<Integer> oddNumbers = filterOdd(numbers);
List<Integer> evenNumbers = filterEven(numbers);


and do something with these lists.

I do two loops over one collection.

Solution 2:

Create one method:

public void filter(List<Integer> numbers, List<Integer> oddNumbers, List<Integer> evenNumbers){
for(Integer n : numbers){
if(n % 2 != 0){
}else{
}
}
}


and in the code I will call:

List<Integer> numbers = Arrays.asList(new Integer[] { 1,2,3,4,5,6,7,8,9 });
List<Integer> oddNumbers  = new ArrayList<Integer>();
List<Integer> evenNumbers = new ArrayList<Integer>();
filter(numbers, oddNumbers, evenNumbers);


I hear that assigning results for parameters is bad practice.

Which solution is better?

• "Disadvantage: I hear that assign results for parameters is a bad practice." <-- where did you read that? If it is the most practical for you, just use it... – fge Jun 24 '13 at 12:51
• I think the first one is better. Not only because it is most reusable, but because the second one can be created also by making calls to the 2 methods of the first solution – py_script Jun 24 '13 at 15:43
• Assigning to parameters is definitely an abomination. But it is not what the OP does here. So that principle, however true, just do not apply here. Calling a method on a parameter is not an assignment. – abuzittin gillifirca Jun 25 '13 at 13:02

Here is a solution which replicates Guava's Predicate interface. Since this interface is really easy, here is how it is done:

public interface Predicate<T>
{
// returns true if input obeys the predicate
boolean apply(T input);
}


Given this interface, implement it for your types; and then do a method like this:

public static <T> List<List<T>> filteredLists(final List<T> list,
final List<Predicate<T>> predicates)
{
final int size = predicates.size();
final List<List<T>> ret = new ArrayList<List<T>>(size);

// Fill ret with initial lists
for (int i = 0; i < size; i++)

// Now walk the predicates and add to the necessary lists

for (final T element: list)
for (int i = 0; i < size; i++)
if (predicates.get(i).apply(element))

// Done! Return...
return ret;
}


Now, if you had a utility class, say MyPredicates, with two methods even() and odd(), you could write:

final List<List<Integer>> filtered = filteredList(inputList,
Arrays.asList(MyPredicates.even(), MyPredicates.odd()));


The "drawback" here is that it is up to the caller to remind what predicates where in what order in the calling lists, of course.

As to your MyPredicates hypothetical class:

public static final class MyPredicates
{
// No instansiation
private MyPredicates()
{
}

public static Predicate<Integer> even()
{
return new Predicate<Integer>()
{
@Override
public boolean apply(final Integer input)
{
return input.intValue() % 2 == 0;
}
}
}

public static Predicate<Integer> odd()
{
return new Predicate<Integer>()
{
@Override
public boolean apply(final Integer input)
{
return input.intValue() % 2 == 1;
}
}
}
}


Go for two methods :

• the signatures make more sense, and you don't need to use parameters as return values
• while this is probably slightly slower, we should remember that "Premature optimization is the root of all evil" (Knuth); unless you can show that having two loops is a performance bottle neck in your code : don't optimize.
• this will be easier to refactor to using the Predicate approach that @fge suggests. Even though at this point that would, in my opinion, be overkill. Nevertheless, a valid option once lambdas are available in 8.
• In fact, Java 8 will have Predicate... They have salvaged quite a few interfaces from Guava for that matter. Hurray! – fge Jun 25 '13 at 10:18

As already mentioned in the other answers, in Java 8 you can use lambdas and the stream API to remove a lot of boilerplate.

Both your solutions can be converted to use streams.

Solution 1 - two methods:

public List<Integer> oddNumbers(List<Integer> numbers) {
return numbers.stream().filter(n -> (n % 2) != 0).collect(Collectors.toList());
}

public List<Integer> evenNumbers(List<Integer> numbers) {
return numbers.stream().filter(n -> (n % 2) == 0).collect(Collectors.toList());
}


Solution 2 - as single method:

public void filter(List<Integer> numbers, List<Integer> oddNumbers, List<Integer> evenNumbers) {
Map<Boolean, List<Integer>> partitions = numbers.stream().collect(Collectors.partitioningBy(n -> (n % 2) == 0));
}

• For solution 1, you can even pass the predicate as a parameter, and then just one method will be enough. – Ankit Soni Jun 17 '18 at 4:57

If you only need to split collection into 2 pieces by a certain criterion, then there's a built-in collector to achieve this:

 final List<Integer> numbers = asList(1, 2, 3, 4, 5);
final Map<Boolean, List<Integer>> lists = numbers.stream().collect(Collectors.partitioningBy(n -> n % 2 == 0));
System.out.println(lists.get(true));
System.out.println(lists.get(false));


This will print:

  [2, 4]
[1, 3, 5]

• Indeed the solution I would go with. Also, in case of more than two options (so not a Boolean), there's also various groupingBy functions. – Simon Forsberg Feb 1 at 14:47