Java собирать в список, но указывать предварительно определенный порядок первых двух элементов - PullRequest
0 голосов
/ 09 декабря 2018

У меня есть List<Person> объекты.Из него я хочу получить список всех идентификаторов, и я всегда хочу, чтобы идентификаторы "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 ]

0 голосов
/ 09 декабря 2018

Вдохновленный Стюартом Марксом, существует еще более простое решение:

List<String> allIds = allPeople.stream()
      .map(Person::getId)
      .distinct()
      .sorted(comparing(x -> !"abc".equals(x)).thenComparing(x -> !"bob".equals(x)))
      .collect(Collectors.toList());
0 голосов
/ 09 декабря 2018

Я предполагаю, что каждый идентификатор встречается в списке только один раз.При этом я бы выбрал простое простое решение:

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");
0 голосов
/ 09 декабря 2018

Кажется, что вам нужно больше 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());
}
0 голосов
/ 09 декабря 2018

если вы хотите выполнить это в «полностью» потоковом конвейере, вы можете сделать:

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» был перед «bob», тогда измените

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