Нужна помощь в модификации метода для слияния элементов коллекции

324
08 марта 2017, 17:55

У меня метод, который сливает элементы коллекции, если поля этих элементов, подходят под определенные условия. И при определенных условиях какие-то из элементов надо удалить прямо на ходу, но при использовании циклов foreach я не могу использовать удаление из коллекции так как натыкаюсь на ConcurrentModificationException. И мне приходится ходить по коллекции двойным циклом используя счетчик, и все время дергать методы get(...).

Проблема заключается в том что операция remove(...) для ArrayList очень дорогая так как надо сдвигать все элементы. А операция get(...) дорогая для LinkedList. Таким образом получается что мне совершенно необходимо избавится либо от одной операции, либо от другой.

И я попал в не приятную вилку: беру LinkedList, и убираю get() - получаю ConcurrentModificationException, беру ArrayList из-за метода remove(...) теряю всю производительность.

Помогите пожалуйста выйти из этой ситуации, так что-бы не терять производительность.

Вот мой код:

@Override
public Collection<Order> automaticDeals(List<Order> orders) {
    for (int i = 0; i < orders.size(); i++) {
        for (int j = 1 ;j < orders.size(); j++) {
            List<Order> forDelete = deal(orders.get(i), orders.get(j));
            for (Order order : forDelete) {
                orders.remove(order);
            }
        }
    }
    return orders;
}
private List<Order> deal(Order fstOrder, Order scdOrder) {
    List<Order> ordersForDelete = new LinkedList<>();
    if (dealIsImpossible(fstOrder, scdOrder)) {
        return ordersForDelete;
    }
    if (fstOrder.getVolume() < scdOrder.getVolume()) {
        int volume = scdOrder.getVolume() - fstOrder.getVolume();
        scdOrder.setVolume(volume);
        ordersForDelete.add(fstOrder);
    } else if (fstOrder.getVolume() > scdOrder.getVolume()) {
        int volume = fstOrder.getVolume() - scdOrder.getVolume();
        fstOrder.setVolume(volume);
        ordersForDelete.add(scdOrder);
    } else if (fstOrder.getVolume() == scdOrder.getVolume()) {
        ordersForDelete.add(fstOrder);
        ordersForDelete.add(scdOrder);
    }
    return ordersForDelete;
}
Answer 1

как я понял, нужна операция distinct по особому полю. Это можно выполнить с помощью java 8 stream api с реализацией своего предиката.

public class Order {
    private int volume;
    private int sec;
    public Order(int volume) {
        this.volume = volume;
    }
    public int getVolume() {
        return volume;
    }
    @Override
    public String toString() {
        return "Order{" +
               "volume=" + volume +
               '}';
    }
}

public static void main(String[] args) {
    List<Order> collect = new ArrayList<Order>() {{
        add(new Order(4));
        add(new Order(1));
        add(new Order(1));
        add(new Order(8));
        add(new Order(1));
        add(new Order(2));
    }};
    List<Order> res = collect.stream().filter(distinctByKey(f -> f.getVolume())).collect(Collectors.toList());
    res.forEach(System.out::println);
}
public static <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
    Map<Object,Boolean> seen = new ConcurrentHashMap<>();
    return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}

результат выполнения:

Order{volume=4}
Order{volume=1}
Order{volume=8}
Order{volume=2}

P.s можно использовать parallel() для ускорения операций

вдохновился источником

READ ALSO
CountDownTimer потребляет много памяти

CountDownTimer потребляет много памяти

В сервисе у меня есть таймер по окончанию которой идет запрос к серверу:

321
Cлушатель для графики OnDraw

Cлушатель для графики OnDraw

Как сделать слушатель для графики OnDraw в MainActivity на нажатие?

256
Как использовать hibernate grails 3,mysql?

Как использовать hibernate grails 3,mysql?

У меня есть mysql сервер и 2 приложения: web (сайт, grails 3) и android приложениеБаза данных содержит расписание матчей

334
Отображение клавиатуры в фрагменте

Отображение клавиатуры в фрагменте

Как в одном из нескольких фрагментов добавить постоянно отображающийся клавиатуру

244