I have a requirement to check whether there is a common element in two lists. I came up with two ways to do this:
Method 01 : Loops
private boolean func01 (List<String> list1, List<String> list2) {
for (String group : list1) {
for (String funcGroup : list2) {
if (group.equals(funcGroup)) {
return true;
}
}
}
return false;
}
Method 02 : Lambda
private boolean func02 (List<String> list1, List<String> list2) {
return list1.stream().filter(list2::contains).findAny().isPresent();
}
In my view, I find the first method more readable. What I need to understand is, whether there are any differences or advantages when comparing these two methods?
method 1 optimization:
you don't need 2 loops and you can return immediately when you have a match, so you stop the traversing of the list at that point - (e.g. sunshine case you get a match on first element - you have 1 iteration, worst case your match is the last element - you have to traverse the whole list to hit that match)
private boolean func01 (List<String> list1, List<String> list2) {
for (String group : list1) {
if (list2.contains(group)) return true;
}
return false;
}
lambda equivalent optimization:
findAny().isPresent()
- get an optional of an element matching the predicate and check if the Optional is present - this is equivalent of anyMatch()
because both expressions return boolean
filter()
will always traverse the whole list
anyMatch()
has a short-circuit behavior - means it will stop on the first matchso you can re-write it as:
private boolean func02 (List<String> list1, List<String> list2) {
return list1.stream().anyMatch(list2::contains);
}
To answer your question - there's no significant performance difference in the two approaches, but take into consideration:
creating stream for a collection has a slight overhead
stream operations can be run in parallel (list1.stream().parallel().anyMatch(list2::contains)
). For example in this case anyMatch()
running in parallel threads over the same stream will periodically check if the previous threads found a match and will stop traversing the collection and not continue traversing the whole collection. So in theory for significantly large input lists, you should get better results with parallel streams.
To answer your direct question, if you want to know whether there's a performance gain by using lambda expressions, you should create a microbenchmark and measure.
However, I would like to point out that your 2nd solution not only uses a lambda expression (actually, a method reference), but also a Stream
. In general, stream-based solutions take longer, due to all the infraestructure needed to run the stream pipeline. However, it also happens that, in most cases, these solutions scale much better.
Now, as to your specific problem, the best way to check whether there is a common element in two lists, is by using the Collections.disjoint
method, available since Java 1.5:
return !Collections.disjoint(list1, list2);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With