【问题标题】:Java collect to list but specify pre-defined first two elements orderJava收集到列表但指定预定义的前两个元素顺序
【发布时间】:2019-05-10 11:46:59
【问题描述】:

我有一个List<Person> 对象。从中我想获得所有 id 的列表,并且我总是希望 id “abc”和“bob”作为列表的第 0 和第 1 个索引(如果可用)。有没有办法用java流做到这一点?

class Person {
   private String id;
}

List<Person> allPeople = ...
List<String> allIds = allPeople.stream().map(Person::id).collect(Collectors.toList());

我的做法是:

Set<String> allIds = allPeople.stream().map(Person::id).collect(Collectors.Set());
List<String> orderedIds = new ArrayList<>();
if(allIds.contains("abc")) {
   orderedIds.add("abc");
}
if(allIds.contains("bob")) {
   orderedIds.add("bob");
}
//Iterate through the set and all add all entries which are not bob and abc in the list.

【问题讨论】:

    标签: java java-8 java-stream comparator


    【解决方案1】:

    您似乎需要更多PriorityQueue 而不是List,所以可能是这样的:

    PriorityQueue<String> pq = list.stream()
                .map(Person::getId)
                .distinct()
                .collect(Collectors.toCollection(() -> new PriorityQueue<>(
                        Comparator.comparing(x -> !"abc".equals(x))
                                .thenComparing(x -> !"bob".equals(x)))));
    

    如果您仍然需要 List,只需将 pq 合并为一个:

    List<String> result = new ArrayList<>();
    while (!pq.isEmpty()) {
       result.add(pq.poll());
    }
    

    【讨论】:

    • 太棒了!我认为您可以在 List.sort() 或 Stream.sorted() 中使用此比较器。实际上是一个类似的比较器,它使用相同的技术比较 Person 对象。
    • @StuartMarks 对,不知道为什么我从 PriorityQueue 开始。谢谢
    【解决方案2】:

    我假设每个 id 在列表中只出现一次。有了这个,我会选择一个简单直接的解决方案:

    List<Person> allPeople = ...;
    List<String> allIds = allPeople.stream().map(Person::id).collect(toCollection(ArrayList::new));
    boolean foundBob = allIds.remove("bob");
    if (foundBob) allIds.add(0, "bob");
    boolean foundAbc = allIds.remove("abc");
    if (foundAbc) allIds.add(0, "abc");
    

    请注意,"bob""abc" 以相反的顺序移至列表的头部。所以"abc"到底是第一位的。

    你可以制作一个移动元素的小实用方法:

    static void moveToHead(List<String> list, String elem) {
      boolean found = list.remove(elem);
      if (found) list.add(0, elem);
    }
    

    有了这个,你的代码更简单,更容易理解:

    List<Person> allPeople = ...;
    List<String> allIds = allPeople.stream().map(Person::id).collect(toCollection(ArrayList::new));
    moveToHead(allIds, "bob");
    moveToHead(allIds, "abc");
    

    【讨论】:

    • 你应该使用toCollection(ArrayList::new),因为toList()不保证返回一个可变的List
    【解决方案3】:

    如果您想在“完全”流管道中执行此操作,您可以这样做:

    allPeople.stream()
             .map(Person::id)
             .distinct()
             .collect(collectingAndThen(partitioningBy(s -> "abc".equals(s) || "bob".equals(s)), 
                        map -> Stream.concat(map.get(true).stream(), map.get(false).stream())));
             .collect(toList());
    

    如果你总是想在“bob”前面加上“abc”,那就改

    map.get(true).stream()
    

    map.get(true).stream()
        .sorted(Comparator.comparing((String s) -> !s.equals("abc")))
    

    您可以做的另一个解决方案是:

    Set<String> allIds = allPeople.stream().map(Person::id).collect(toSet());
    List<String> orderedIds = Stream.concat(allIds.stream()
                            .filter(s -> "abc".equals(s) || "bob".equals(s))
                            .sorted(Comparator.comparing((String s) -> !s.equals("abc"))),
                    allIds.stream().filter(s -> !"abc".equals(s) && !"bob".equals(s)))
                    .collect(toList());
    

    这与上述partitioningBy 几乎相同,只是方法不同。


    最后,您可能会感到惊讶,但您的方法实际上看起来不错,因此您可能希望通过以下方式完成它:

    Set<String> allIds = allPeople.stream().map(Person::id).collect(toSet());
    
    List<String> orderedIds = new ArrayList<>();
    
    if(allIds.contains("abc")) 
        orderedIds.add("abc");
    
    if(allIds.contains("bob")) 
        orderedIds.add("bob");
    
    orderedIds.addAll(allIds.stream().filter(s -> !"abc".equals(s) && ! "bob".equals(s)).collect(toList()));
    

    【讨论】:

    • 甜蜜。这很有趣。 abc 在 Bob 之前的保证是什么?
    • @user1692342 如果您使用第一种方法并使用提供的比较器,那么“abc”将始终位于“bob”之前,而第二种方法将实现相同的效果。
    【解决方案4】:

    受 Stuart Marks 的启发,有一个更简单的解决方案:

    List<String> allIds = allPeople.stream()
          .map(Person::getId)
          .distinct()
          .sorted(comparing(x -> !"abc".equals(x)).thenComparing(x -> !"bob".equals(x)))
          .collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 2012-02-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多