In Java 8, with threads, everything is pretty simple. UPDATE: can be efficient without threads, see below.
List<String> listA = Arrays.asList("2009-05-18","2009-05-19","2009-05-21"); List<String> listB = Arrays.asList("2009-05-18","2009-05-18","2009-05-19","2009-05-19", "2009-05-20","2009-05-21","2009-05-21","2009-05-22"); List<String> result = listB.stream() .filter(not(new HashSet<>(listA)::contains)) .collect(Collectors.toList());
Note that a hash set is created only once: a method reference is bound to its containing method. To do the same with lambda, one would have to have a set in a variable. Creating a variable is not a bad idea, especially if you find it unsightly or difficult to understand.
You cannot easily disprove a predicate without anything like this helper method (or explicit cast), since you cannot directly call a reference to the negation method (type inference is necessary first).
private static <T> Predicate<T> not(Predicate<T> predicate) { return predicate.negate(); }
If the threads had a filterOut method or something like that, it would look better.
Also, @Holger gave me an idea. ArrayList has a removeAll method, optimized for repeated deletions, it only permutes its elements once. However, it uses the contains method provided by this collection, so we need to optimize this part if listA is not tiny at all.
With the previously declared listA and listB this solution does not require Java 8, and it is very efficient.
List<String> result = new ArrayList(listB); result.removeAll(new HashSet<>(listA));