19

I want to compare if any of the objects in a list2 is present in a list1.

I could iterate over both lists and compare all elements using .contains() but I am wondering if there is not a more efficient way. I found this and I am trying to implement the suggested method:

List<Item> list1;
List<Item> list2;

boolean anyMatch = list1.stream().anyMatch(x -> x.equals(list2.stream()));
System.out.println(anyMatch);

When I do this I constantly get false, even when I'd expect a true. How come?

Community
  • 1
  • 1
k88
  • 1,858
  • 2
  • 12
  • 33
  • Steams have a lot of scaffolding to keep state for lazy evaluation. – Thorbjørn Ravn Andersen Apr 16 '16 at 22:39
  • 1
    You are comparing an element in list1 with the stream of list2. They are not even of the same type. You won't make anything faster with streams. you could use list1.equals(list2) but order will matter. Use Set instead of List if order doesn't matter and items are unique. – Sason Ohanian Apr 16 '16 at 22:40
  • @Tunaki Should have been list, my bad. I want to compare if any of the objects in list2 is present in list1. Should have been clearer. – k88 Apr 16 '16 at 22:42
  • How would you do this with iteration? Show an example so we can more clearly understand. – Radiodef Apr 16 '16 at 22:45

3 Answers3

27

From your comments, you have two lists, list1 and list2. You want to find out if at least one of the element in list2 is contained in list1.

With the Stream API, you can acquire a Stream of list2. Then, a call anyMatch(predicate) returns whether one of the element of this stream matches the given predicate, which, in this case, tests whether the element is contained in list1.

boolean anyMatch = list2.stream().anyMatch(list1::contains);

This uses a method reference as the predicate.

You would have better performance by converting the list1 into a Set, which guarantees constant-time look-up:

boolean anyMatch = list2.stream().anyMatch(new HashSet<>(list1)::contains);
Tunaki
  • 132,869
  • 46
  • 340
  • 423
14

Although @Tunaki's answer is correct, here's another more concise way (it doesn't use Stream.anyMatch() method, though):

boolean anyMatch = !Collections.disjoint(list1, list2);

This uses the Collections.disjoint() method, which returns true when two collections have no elements in common.

Tunaki's comment regarding performance also applies here: for better performance, the best would be to turn your list1 into a HashSet, because its contains() method is O(1) average. The Collections.disjoint() method actually checks if any of its arguments is a Set and iterates over the collection that is not a Set. So in your case, all you have to do is create a HashSet from your list1:

boolean anyMatch = !Collections.disjoint(new HashSet<>(list1), list2);

Note: After all, my answer is only 5 characters shorter that Tunaki's :)

Community
  • 1
  • 1
fps
  • 33,623
  • 8
  • 55
  • 110
  • Thanks for showing another way for achieving this. I take it that both methods have an equal performance if I convert a list into a `HashSet`? – k88 Apr 17 '16 at 14:42
  • 1
    @k88 They have equal performance if the stream is sequential. If it's parallel, it will perform better when lists have many elements (a couple thousands or more, you would have to test it, depends on the machine). Also, for best performace, you should convert the list which has more elements to a `HashSet` and iterate over the other one. – fps Apr 17 '16 at 17:05
9
  • Using Java 8 stream api
boolean isAnyMatch = list2.stream().anyMatch(list1::contains);
  • Using Collection class method
boolean isAnyMatch = !Collections.disjoint(new HashSet(list1), list2);

Anyway converting list to set before comparing will give faster output.

MrPk
  • 2,862
  • 2
  • 20
  • 26
TanvirChowdhury
  • 2,498
  • 23
  • 28