2017-03-08 25 views
3

我有用戶對象的列表,定義如下:Java流:獲得最新版本的用戶記錄

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實現此目的的最佳方法?

+2

您是否嘗試過使用'Comparator'對'list'進行排序? –

回答

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的映射函數產生val的UE
  3. mergeFunction合併函數,用於解析使用相同的密鑰相關聯,如提供給Map.merge(對象,對象,雙功能)
  4. mapSupplier它返回一個新的功能,空值之間的碰撞映射到其結果將被插入

  1. 第一個參數是用戶:: getUserId()來獲取密鑰。
  2. 第二個參數是一個返回User對象的函數。
  3. 第三個參數是一個通過比較和保持用戶最新版本來解決衝突的功能。
  4. 第四個參數是HashMap的「新」方法。
0

這將是痛苦的,但它可以與一些聚合來實現,在Java 8流框架:

// 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,檢查地圖中是否已經包含了用戶的字符串表示
  • 如果沒有,或者所映射的用戶具有較低的版本號,存儲用戶在地圖中。
+0

如果用戶類有一個ID字段(而不是按名稱+姓氏分組),會有用嗎? –

+0

@NickMelis是的,絕對。這可以消除誤報(John Smith案例) –

+2

您可以使用'Collector.toCollection(() - >新的TreeSet <>(Comparator.comparing(User :: getVersionNumber))'而不是'Collector。 new TreeSet <>(Comparator.comparing(User :: getVersionNumber)),Set :: add,(left,right) - > {left.addAll(right); return left;}) – Holger

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)); 

請語法其粗糙的

6

隨着一點點的援助this answer檢查:

Collection<User> latestVersions = users.stream() 
      .collect(Collectors.groupingBy(User::getUserId, 
        Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(User::getVersionNumber)), Optional::get))) 
        .values(); 

我假設常用的getter。結果:

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

我已經添加了一個唯一的userId來代替fullName這裏。同樣的概念雖然 –

+0

運行你的代碼到一個JUnit測試給我一個錯誤:java.lang.NullPointerException:元素不能映射到一個空密鑰 –

+2

@Nick Melis:好吧,異常消息已經說過了,'null'鍵是不允許的,所以'fullName','userId'屬性一定不能是'null'。 – Holger

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; 
} 

,然後我可以使用下面的流:

users.stream().sorted((u1, u2) -> u2.versionNumber - u1.versionNumber) 
       .filter(distinctByKey(u -> u.name + u.surname)) 
       .collect(Collectors.toList()); 

還有其他一些很好的解決方案,以做它可以是一個關鍵的獨特基地發現於Java 8 Distinct by property

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