优雅的方法将对象移动到列表末尾

5

如果一个对象的布尔标志设置为true,我想把它移动到列表的末尾。以下代码可以实现这个功能,即删除并重新添加该对象。在Java 8中是否有更好、更优雅的方式来完成此操作?我必须处理这个布尔标志以确定对象是否需要移到列表末尾。请给出建议。

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

创建一个比较器,当标志为“false”时简单地返回0,当标志为“true”时返回1... 然后排序。 - RobOhRob
1
@user 嗯?... - RobOhRob
4个回答

4

这不太优美:

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

2
Stream.of(partitionBy.get(false), partitionBy.get(true))创建了一个Stream<List<Item>>。你可以在之后应用flatMap(List::stream),或者一开始就使用Stream.concat(partitionBy.get(false).stream(),partitionBy.get(true).stream()) - Holger

3

我认为这比排序整个列表更有效,因为对于ArrayList而言,它的时间复杂度为O(n)。

你可以使用这种方法查找所有与该谓词匹配的对象并将其移动到末尾。调用remove方法时使用索引而不是对象本身,以便如果它是ArrayList而不是LinkedList,列表不必再次内部遍历。正如Holger所指出的那样,第一种方法在处理多个对象时非常低效,虽然它更加简洁。

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

另一种更加高效但需要两行代码的方法:

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

这将会短路并且如果找到所寻找的对象,不会遍历整个列表。
对于一个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);

这仅适用于将单个值移动到末尾。 - RobOhRob
1
挖掘变化 - RobOhRob
3
对于单个元素,对 ArrayList 执行 list.add(list.remove(i)) 的时间复杂度为 O(n)。当你需要对列表中的未知数量元素执行此操作时,你可能需要重复这个过程最多 n 次,使整个操作的时间复杂度变为 O(n²),这比排序要慢得多。 - Holger

1
为了回答如何将元素移动到列表末尾的问题,我认为最优雅的方法是对子列表进行旋转。在您的情况下,可以这样做:
Collections.rotate(items.subList(i, items.size()), -1);

0

你可以使用流进行排序

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

2
或者可以直接通过 items.sort(...) 对列表进行排序。顺便说一下,无需将由 Arrays.asList(...) 创建的 List 复制到 ArrayList 中。 - Holger

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接