2

Basically, I'd like to use a Predicate to filter a generic (which extends Collection), and then return an instance of the same generic Collection implementation (preferably a new instance) e.g. implement the method signature F removeNulls(F inputs).

I have the following examples, but there are caveats to each (removeNulls4 is probably the closest to what I'm trying to achieve):

Caveats

removeNulls1:

  • the returned list may(/will) not be an instance of F (requires casting)

removeNulls2:

removeNulls3:

removeNulls4:

  • modifies the original list
import java.util.*;
import java.util.function.*;
import java.util.stream.*;

public class QuickTest<I, F extends Collection<I>> {

    Predicate<I> removeNullsPredicate = x -> x != null;

    @SuppressWarnings("unchecked")
    public F removeNulls1(F inputs) throws Exception {
        return (F) inputs.stream().filter(removeNullsPredicate)
                .collect(Collectors.toList());
    }

    @SuppressWarnings("unchecked")
    public F removeNulls2(F inputs) throws Exception {
        F forReturn = (F) inputs.getClass().newInstance();
        inputs.stream().filter(removeNullsPredicate)
                .collect(Collectors.toCollection(() -> forReturn));
        return forReturn;
    }

    public F removeNulls3(F inputs) throws Exception {
        Iterator<I> iter = inputs.iterator();
        while (iter.hasNext()){
            I next = iter.next();
            boolean test = removeNullsPredicate.test(next);
            if (!test){
                iter.remove();
            }
        }
        return inputs;
    }

    public F removeNulls4(F inputs) throws Exception {
        List<I> forRemoval = inputs.stream().filter(removeNullsPredicate.negate())
                .collect(Collectors.toList());
        inputs.removeAll(forRemoval);
        return inputs;
    }
}
Community
  • 1
  • 1
Nick Grealy
  • 24,216
  • 9
  • 104
  • 119
  • 1
    I'd say creating a new instance of the collection is an implementation detail which should not be handled by generic code. I'd think about either extending the Collection interface to force the implementation to provide a custom Collector or forcing the 'user' of the predicate to supply a Collector by any other means. – samjaf Apr 06 '16 at 05:04

1 Answers1

3

You could provide a Supplier<F> as an argument:

public F removeNulls(F inputs, Supplier<F> factory) {
    return inputs.stream().filter(removeNullsPredicate)
            .collect(Collectors.toCollection(factory));
}

Then simply call:

List<I> nonNulls = removeNulls(inputs, ArrayList::new);
fps
  • 33,623
  • 8
  • 55
  • 110