我有一个User对象列表,定义如下:
public class User {
private String userId; // Unique identifier
private String name;
private String surname;
private String otherPersonalInfo;
private int versionNumber;
}
public User(String userId, String name, String surname, String otherPersonalInfo, int version) {
super();
this.name = name;
this.surname = surname;
this.otherPersonalInfo = otherPersonalInfo;
this.version = version;
}
}
示例列表:
List<User> users = Arrays.asList(
new User("JOHNSMITH", "John", "Smith", "Some info", 1),
new User("JOHNSMITH", "John", "Smith", "Updated info", 2),
new User("JOHNSMITH", "John", "Smith", "Latest info", 3),
new User("BOBDOE", "Bob", "Doe", "Personal info", 1),
new User("BOBDOE", "Bob", "Doe", "Latest info", 2)
);
我需要一种过滤此列表的方法,以便我只获得每个用户的最新版本,即:
{"JOHNSMITH", "John", "Smith", "Latest info", 3},
{"BOBDOE", "Bob", "Doe", "Latest info", 2}
使用Java8 Stream API实现这一目标的最佳途径是什么?
答案 0 :(得分:6)
答案 1 :(得分:1)
我首先按版本排序,以确保最新的条目在列表中排在第一位。然后我过滤了一个不同的键,以确保只有一个匹配此键的对象是结果的一部分。对于过滤,我需要一个谓词,它存储一个状态来过滤已经看过的东西。
谓词看起来像这样:
private static <T> Predicate<T> distinctByKey( Function<? super T, ?> key ) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent( key.apply( t ), Boolean.TRUE ) == null;
}
然后我可以使用以下Stream:
users.stream().sorted( ( u1, u2 ) -> u2.versionNumber - u1.versionNumber )
.filter( distinctByKey( u -> u.name + u.surname ) )
.collect( Collectors.toList() );
还有一些其他不错的解决方案可以在Java 8 Distinct by property上找到一个明确的密钥基础。
答案 2 :(得分:1)
HashMap<String, User> map = users.stream().collect(Collectors.toMap(User::getUserId,
e -> e,
(left, right) -> {return left.getVersion() > right.getVersion() ? left : right;},
HashMap::new));
System.out.println(map.values());
以上代码打印:
[User [userId=BOBDOE, name=Bob, surname=Doe, otherPersonalInfo=Latest info, version=2], User [userId=JOHNSMITH, name=John, surname=Smith, otherPersonalInfo=Latest info, version=3]]
说明: toMap方法有4个参数:
答案 3 :(得分:0)
这将是痛苦的,但可以通过Java 8 Streams框架中的一些聚合来完成:
// create a Map from user name to users, sorted by version
Map<String, NavigableSet<User>> grouped =
users.stream()
.collect(
Collectors.groupingBy(
u -> u.name + "," + u.surname,
HashMap::new,
Collectors.toCollection(
() -> new TreeSet<>(
Comparator.comparing(
User::getVersionNumber)))));
// retrieve the latest versions from the Map
List<User> latestVersions = grouped.entrySet()
.stream()
.map(e -> e.getValue().last())
.collect(Collectors.toList());
考虑到这是多么冗长,我可能会选择一个必要的解决方案。
Map<String, User>
User
,检查地图是否已包含用户的字符串表示答案 4 :(得分:0)
在 java 8 中,您可以以lambda表达式的形式创建比较器。
通过比较器调用users.stream().sorted
。
示例:
Comparator<User > byVersionNumber = (u1, u2) -> Integer.compare(
u1.getversionNumber(), u2.getversionNumber());
users.stream().sorted(byVersionNumber)
.forEach(u -> System.out.println(u));
请检查其粗略的语法
答案 5 :(得分:0)
List<User> users = Arrays.asList(
new User("JOHNSMITH", "John", "Smith", "Some info", 1),
new User("JOHNSMITH", "John", "Smith", "Updated info", 2),
new User("JOHNSMITH", "John", "Smith", "Latest info", 3),
new User("BOBDOE", "Bob", "Doe", "Personal info", 1),
new User("BOBDOE", "Bob", "Doe", "Latest info", 2)
).stream()
.collect(Collectors.collectingAndThen(
Collectors.toMap(
User::getUserId, //The user's unique property
Function.identity(), //Function<User, User>
BinaryOperator.maxBy(Comparator.comparing(User::getVersionNumber))
),
map -> (List)map.values()
));