Элегантный способ переместить объект в конец списка - PullRequest
5 голосов
/ 08 мая 2020

Я пытаюсь переместить объект в конец списка, если для него установлен логический флаг true. Следующие работы, где я иду по пути удаления и добавления обратно. Есть ли более элегантный способ сделать это в Java 8? Я должен работать с этим логическим флагом, чтобы определить, нужно ли объекту go в конец списка. Пожалуйста посоветуй.

public class Main {

    public static void main(String[] args) {

        Item item1 = new Item();
        item1.setName("item1");
        Item item2 = new Item();
        item2.setName("item2");
        item2.setMoveToLast(true);
        Item item3 = new Item();
        item3.setName("item3");
        Item item4 = new Item();
        item4.setName("item4");

        List<Item> items = new ArrayList<>(Arrays.asList(item1, item2, item3, item4));
        System.out.println("Before moving...");
        items.forEach(System.out::println);

        // only item2 has flag set to true thus only item2 will be sent to end of list.  
        move(items);
        System.out.println("After moving...");
        items.forEach(System.out::println);
    }

    private static void move(List<Item> items){
        for (int i = 0; i < items.size(); i++) {
            Item item = items.get(i);
            if (item.isMoveToLast()){
                items.remove(item);
                items.add(item);
            }
        }
    }
}

@Getter
@Setter
class Item {
    private int order;
    private String name;
    private boolean isMoveToLast;

    @Override
    public String toString() {
        return "Item{" +
                "name='" + name + '\'' +
                ", isMoveToLast=" + isMoveToLast +
                '}';
    }
}

Ответы [ 4 ]

4 голосов
/ 08 мая 2020

Это не элегантно:

Map<Boolean,List<Item>> partitionBy =  items.stream()
            .collect(Collectors.partitioningBy(Item::isMoveToLast));

Stream.of(partitionBy.get(false),partitionBy.get(true)).flatMap(List::stream)
            .collect(Collectors.toList());

Или на основе комментария @ Holger :

Stream.concat(partitionBy.get(false).stream(), partitionBy.get(true).stream())
            .collect(Collectors.toList());
3 голосов
/ 08 мая 2020

Я считаю, что это более эффективно, чем сортировка всего списка, потому что это O (n) для ArrayList s.

Вы можете найти все объекты, соответствующие этому предикату, и перейти к закончить с этим. Метод удаления вызывается с индексом, а не с самим объектом, так что список не должен go снова проходить через него внутри, если это ArrayList, а не LinkedList. Этот первый более краток, но довольно неэффективен с несколькими объектами, как указал Хольгер.

IntStream.range(0, list.size())
    .filter(i -> list.get(i).isMoveToLast())
    .foreach(i -> list.add(list.remove(i)));

Другой способ сделать это, который немного более эффективен, но требует 2 строки вместо 1:

List<Item> newList = new ArrayList<>(list.size());
newList.addAll(
    list.stream()
         .filter(it -> it.isMoveToLast() || !newList.add(it))
         .collect(Collectors.toList()));

Если вы хотите переместить только один объект в конец, вы также можете сделать

IntStream.range(0, list.size())
    .filter(i -> list.get(i).isMoveToLast())
    .findFirst()
    .ifPresent(i -> list.add(list.remove(i)));

Это приведет к короткому замыканию, а не go по всему списку, если он найдет объект, на котором он ищу.

Для LinkedList вы могли бы это сделать, но это не требует Java 8:

Iterator<Item> iter = list.iterator();
List<Item> toAdd = new ArrayList<>();
while (iter.hasNext()) if (iter.next().isMoveToLast()) toAdd.add(iter.remove());
for (Item it : toAdd) list.add(it);
1 голос
/ 08 мая 2020

Чтобы ответить на вопрос о перемещении элемента в конец списка, я думаю, что самый элегантный способ - это повернуть подсписок. В вашем случае это можно сделать так:

Collections.rotate(items.subList(i, items.size()), -1);
0 голосов
/ 08 мая 2020

Можно сортировать по потоку

List<Item> items = new ArrayList<>(Arrays.asList(item1, item2, item3, item4));

items = items.stream()
        .sorted((i1,i2) -> Boolean.compare(i1.isMoveToLast(),i2.isMoveToLast()))
        .collect(Collectors.toList());
...