简体   繁体   中英

Elegant way to move Object to end of list

I am trying to move an object to the end of the list if it has a boolean flag set to true. The following works where I am taking the route of deleting it and adding it back. Is there a better more elegant way to do this in Java 8? I have to work with that boolean flag to identify if object needs to go to end of list. Please advice.

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

This is not elegant:

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

Or based on @Holger 's comment:

Stream.concat(partitionBy.get(false).stream(), partitionBy.get(true).stream())
            .collect(Collectors.toList());

I feel this is more efficient than sorting the entire list because it's O(n) for ArrayList s.

You can find all objects matching that predicate and move to the end with this. The remove method's called with the index instead of the object itself so that the list does not have to go through it internally again if it's an ArrayList and not a LinkedList . This first one is more concise but pretty inefficient with multiple objects, as Holger pointed out.

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

Another way to do it that's a bit more efficient but takes 2 lines instead of 1:

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

If you only want to move a single object to the end, you can also do

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

This will short-circuit and not go through the entire list if it finds the object it's looking for.

For a LinkedList you could do this, but it doesn't require 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);

To answer the question of moving an element to the end of the list, I think the most elegant way is to do a rotate on a sublist. In your case this can be done like this:

Collections.rotate(items.subList(i, items.size()), -1);

You could sort is with stream

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM