Java Stream:获取最新版本的用户记录

时间:2017-03-08 12:13:00

标签: java java-8 java-stream

我有一个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实现这一目标的最佳途径是什么?

6 个答案:

答案 0 :(得分:6)

serverfault的帮助下:

[John Smith Latest info 3, Bob Doe Latest info 2]

我假设通常的吸气剂。结果:

{{1}}

答案 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个参数:

  1. keyMapper 生成密钥的映射函数
  2. valueMapper 用于生成值的映射函数
  3. mergeFunction 一个合并函数,用于解决与同一个键关联的值之间的冲突,如提供给Map.merge(Object,Object,BiFunction)
  4. mapSupplier 一个函数,它返回一个新的空Map,结果将插入其中
    1. 第一个arg是User :: getUserId()获取密钥。
    2. 第二个arg是一个按原样返回User对象的函数。
    3. 第三个arg是一个通过比较和保持用户与最新版本来解决冲突的功能。
    4. 第四个arg是HashMap的“新”方法。

答案 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() 
                ));