首页 文章

Java收集列表但指定预先定义的前两个元素顺序

提问于
浏览
3

我有 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.

4 回答

  • 2

    看起来你需要更多 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());
    }
    
  • 1

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

    如果你总是想在“鲍勃”面前“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()));
    
  • 1

    我假设每个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");
    

相关问题