Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java 8 stream unique Integers

Can i reduce the following code to one/two line?

DTO dto;
List<DTO> dtos;
List<Integer> list1 = dtos.stream().map(DTO::getFirstId).distinct().collect(Collectors.toList());
List<Integer> list2 = dtos.stream().map(DTO::getSecondId).distinct().collect(Collectors.toList());

List<Integer> reducedId = list1.stream().filter(list2::contains).collect(Collectors.toList());
like image 235
seba21007 Avatar asked Feb 24 '17 13:02

seba21007


People also ask

How do I get unique values in stream?

Processing only Unique Elements using Stream distinct() and forEach() Since distinct() is a intermediate operation, we can use forEach() method with it to process only the unique elements.

How do you remove duplicate elements in a stream?

You can use the Stream. distinct() method to remove duplicates from a Stream in Java 8 and beyond. The distinct() method behaves like a distinct clause of SQL, which eliminates duplicate rows from the result set.

Does Java 8 support streams?

Java 8 offers the possibility to create streams out of three primitive types: int, long and double. As Stream<T> is a generic interface, and there is no way to use primitives as a type parameter with generics, three new special interfaces were created: IntStream, LongStream, DoubleStream.


2 Answers

Using a single Java 8 stream is not a great choice here. Instead you should first create a Set so that you can perform an efficient contains test.

Set<Integer> secondIds = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
List<Integer> reducedIds = dtos.stream().map(DTO::getFirstId).distinct()
        .filter(secondIds::contains).collect(Collectors.toList());
like image 199
Patrick Parker Avatar answered Oct 16 '22 18:10

Patrick Parker


You may force it into one stream operation, but the performance would be even worse than what you have now, i.e. an operation with quadratic time complexity.

A better approach would be:

Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
List<Integer> result = dtos.stream().map(DTO::getFirstId)
    .distinct().filter(set::contains).collect(Collectors.toList());
// result now contains the common IDs

By collecting the second IDs into a Set instead of a List, you don’t need to use distinct() in the first stream operation and avoid the linear search applied to every element in the second stream operation when contains is invoked.

You may generally consider using a Set for remembering unique IDs. When using a Set as result type, you may avoid the distinct() of the second stream operation too:

Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
Set<Integer> result = dtos.stream().map(DTO::getFirstId)
    .filter(set::contains).collect(Collectors.toSet());

If you suspect lots of duplicate IDs and want to keep the behavior of sorting out duplicates before checking the other Set, you may use:

Set<Integer> set = dtos.stream().map(DTO::getSecondId).collect(Collectors.toSet());
Set<Integer> result = dtos.stream().map(DTO::getFirstId)
    .collect(Collectors.toCollection(HashSet::new));
result.retainAll(set);

Note that if you prefer long, hard too read “one liner”, you can inline the set variable in all variants.

like image 38
Holger Avatar answered Oct 16 '22 18:10

Holger