Как я могу эффективно переносить объекты из одной коллекции Java в другую в соответствии с предикатом? - PullRequest
0 голосов
/ 12 сентября 2018

Прежде всего, я надеюсь, что этот вопрос еще не задавался.Я посмотрел немного и не смог найти подходящий ответ: s

Я ищу эффективный способ перемещения некоторых объектов из одной коллекции в другую, когда определенное условие выполняется.

В настоящее время я бы сделал это довольно простым способом, но, боюсь, это может быть неоптимальным:

Collection<Object> myFirstCollection;  //let's consider it instanciated and populated
Collection<Object> mySecondCollection; //same for this one

myFirstCollection.stream().forEach(o -> { 
    if ( conditionReturningTrue(o) ) {
        mySecondCollection.add(o);
        myFirstCollection.remove(o);
    }
});

Знаете ли вы какой-нибудь лучший / более эффективный способ сделать это?

Ответы [ 4 ]

0 голосов
/ 12 сентября 2018

Прежде всего, вы должны стремиться к правильности. Для большинства коллекций запрещено изменять исходную коллекцию при ее повторении. Вы можете получить ConcurrentModificationException во время попытки, но даже если это произойдет без исключения, код все равно будет неправильным. Просто эта ошибка не всегда обнаруживается (это проверка с максимальным усилием, чтобы избежать потери производительности). Это относится к forEach(…), а также к stream().forEach(…) и циклу for-each (for(variable declaration: collection))

Единственная поддержка для удаления элементов во время итерации - вручную Iterator использование:

for(Iterator<Object> it = myFirstCollection.iterator(); it.hasNext(); ) {
    Object o = it.next();
    if(conditionReturningTrue(o)) {
        it.remove();
        mySecondCollection.add(o);
    }
}

Альтернативой являются массовые методы.

Во-первых, как показано в this и , которые отвечают, создавая копию всех элементов, которые должны быть переданы первыми.

Во-вторых, вы можете использовать

myFirstCollection.removeIf(o -> conditionReturningTrue(o) && mySecondCollection.add(o));

В реализации default removeIf используется Iterator в цикле, аналогичном приведенному выше. Однако такие коллекции, как ArrayList, предоставляют собственную реализацию removeIf, чтобы преодолеть квадратичную временную сложность цикла Iterator.

0 голосов
/ 12 сентября 2018

Вы можете улучшить производительность, избегая removeAll (что может потребовать квадратичного времени для некоторых Collection с, в которых поиск объекта требует линейного поиска, например List с), используя Collectors.partitioningBy для разделенияоригинал Collection на две List с:

Collection<Object> myFirstCollection;  //let's consider it instanciated and populated
Collection<Object> mySecondCollection; //same for this one

Map<Boolean,List<Object>> partition = 
    myFirstCollection.stream()
                     .collect(Collectors.partitioningBy(o -> conditionReturningTrue(o)));
myFirstCollection.clear();
myFirstCollections.addAll(partition.get(false));
mySecondCollection.addAll(partition.get(true));

С другой стороны, это решение может быть менее эффективным, если только несколько элементов следует переместить с myFirstCollection на mySecondCollection.

0 голосов
/ 12 сентября 2018

Вы уже получили хороший ответ от YCF_L здесь: https://stackoverflow.com/a/52295144/9568238

Но я просто хотел бы добавить, что если вы выберете метод forEach, как вы описываете в своем вопросе, тоstream() является избыточным.Вы можете просто сделать myFirstCollection.forEach(...)

В любом случае, я бы пошел с упомянутым ответом.

0 голосов
/ 12 сентября 2018

Чтобы сделать его более читабельным, в этой ситуации можно использовать Collection::addAll и Collection::removeAll, ваш код может быть:

// create a new Collection where you use filter to search only the Object you want
Collection<Object> filterdCollection = myFirstCollection.stream()
        .filter(o -> conditionReturningTrue(o))
        .collect(Collectors.toCollection(LinkedHashSet::new));

// use allAll to add all the filtered Object to the second collection
mySecondCollection.addAll(filterdCollection);
// use removeAll to remove all the filtered Object from the first collection
myFirstCollection.removeAll(filterdCollection);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...