Reputation: 101
How can I use a stream from two lists to get a list of unique entities? Match only by username
public class Entity {
private String username;
private String password;
}
var first = Arrays.asList(
new Entity("user1", ""),
new Entity("user2", "")
new Entity("user3", "pass3"),
new Entity("user5", "pass5")
);
var second = Arrays.asList(
new Entity("user1", "pass1"),
new Entity("user2", "pass2"),
);
public static void foo(List<Entity> first, List<Entity> second) {
List<Entity>result = Stream.of(first, second)
.flatMap(List::stream)
?
?
.collect(Collectors.toList());
}
result must be list with Entity("user3", "pass3") and Entity("user5", "pass5")
Upvotes: 3
Views: 371
Reputation: 3758
lambda as a straight forward solution
concat the streams of first-list-entities not contained in second-list and vice versa
List<Entity> unique = Stream.concat(
first.stream().filter(e -> ! second.contains( e )),
second.stream().filter(e -> ! first.contains( e )) ).collect( toList() );
Upvotes: 0
Reputation: 2202
Along with using groupingBy you can also use Collectors.toMap with merging (val1, val2) -> null to exclude elements getting to merge thus leaving only single elements:
List<Entity> result = Stream.concat(first.stream(), second.stream())
.collect(Collectors.toMap(Entity::getUsername,
val -> val, (val1, val2) -> null))
.values().stream()
.collect(Collectors.toList());
Upvotes: 3
Reputation: 1403
Probably it's not the best way
public static List<Entity> foo(List<Entity> first, List<Entity> second) {
List<Entity> arr = new ArrayList<>();
arr.addAll(first);
arr.addAll(second);
return arr
.stream()
.filter(entity -> (first.stream().map(Entity::getUsername).anyMatch(username -> username.equals(entity.getUsername())) &&
second.stream().map(Entity::getUsername).noneMatch(username -> username.equals(entity.getUsername()))) ||
(second.stream().map(Entity::getUsername).anyMatch(username -> username.equals(entity.getUsername())) &&
first.stream().map(Entity::getUsername).noneMatch(username -> username.equals(entity.getUsername()))))
.collect(Collectors.toList());
}
The logic in the filter is "Exclusive OR", as we don't have a straight way of doing that, we need to make the logic of (Condition1 and not Condition2) or (Condition2 and not Condition1).
Upvotes: 0
Reputation: 2860
you can make grouping by username:
var groupedData = Stream.concat(list1.stream(), list2.stream())
.collect(Collectors.groupingBy(Entity::getUsername));
and then filtered entity which size > 1:
groupedData.values().stream()
.filter(s -> s.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
or only one a big stream:
Stream.concat(list1.stream(), list2.stream())
.collect(Collectors.groupingBy(Entity::getUsername)).values().stream()
.filter(s -> s.size() == 1)
.flatMap(Collection::stream)
.collect(Collectors.toList());
Upvotes: 6