Reputation: 71
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());
Upvotes: 7
Views: 9452
Reputation: 1254
With StreamEx:
Set<Integer> set = StreamEx.of(dtos).map(DTO::getSecondId).toSet();
List<Integer> result = StreamEx.of(dtos).map(DTO::getFirstId)
.filter(set::contains).distinct().toList();
Or by abacus-common
Set<Integer> set = Stream.of(dtos).map(DTO::getSecondId).toSet();
List<Integer> result = Stream.of(dtos).map(DTO::getFirstId)
.filter(set::contains).distinct().toList();
// OR:
List<Integer> result = Stream.of(dtos).map(DTO::getFirstId)
.intersection(Stream.of(dtos).map(DTO::getSecondId).toSet()).toList();
Upvotes: 2
Reputation: 6706
If you're open to using a third-party library, you have a few options using Eclipse Collections. If you want to use Stream
, the following should work using Collectors2
.
MutableList<Integer> result =
dtos.stream().map(DTO::getFirstId).collect(Collectors2.toSet())
.intersect(dtos.stream().map(DTO::getSecondId).collect(Collectors2.toSet()))
.toList();
Eclipse Collections also has its only LazyIterable
type that can be used instead of Stream
.
LazyIterable<DTO> iterable = LazyIterate.adapt(dtos);
MutableList<Integer> result =
iterable.collect(DTO::getFirstId).toSet()
.intersect(iterable.collect(DTO::getSecondId).toSet())
.toList();
Finally, if you have large numbers of ids, you might want to use primitive sets to avoid boxing Integer
objects. You can either work with Stream
and Collectors2
again as follows:
IntSet second = dtos.stream().collect(
Collectors2.collectInt(DTO::getSecondId, IntSets.mutable::empty));
IntList result = dtos.stream().collect(
Collectors2.collectInt(DTO::getFirstId, IntSets.mutable::empty))
.select(second::contains).toList();
Or you can use LazyIterable
as follows:
LazyIterable<DTO> iterable = LazyIterate.adapt(dtos);
IntList result =
iterable.collectInt(DTO::getFirstId).toSet()
.select(iterable.collectInt(DTO::getSecondId).toSet()::contains)
.toList();
Note: I am a committer for Eclipse Collections.
Upvotes: 3
Reputation: 298389
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.
Upvotes: 4
Reputation: 4957
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());
Upvotes: 11
Reputation: 516
I think you could do something like
List<Integer> reducedId = dtos.stream().map(DTO::getFirstId).distinct().filter(
(dtos.stream().map(DTO::getSecondId).distinct().collect(Collectors.toList()))::contains
).collect(Collectors.toList());
Not tested in my local, but it seems reasonable to me :)
Upvotes: 1