У меня есть два списка:
private final List<EventTeaserModel> events = new ArrayList<>();
private final List<EventTeaserModel> premiumEventList = new ArrayList<>();
Вариант использования моего метода removePremiumEventsFromEvents() заключается в удалении premiumEventList objects из списка событий (удаление объектов с одинаковым путем).
Вот что я пробовал, и это работает. Есть ли лучший способ сделать это на Java?
private void removePremiumEventsFromEvents() {
for (EventTeaserModel premiumEvent: premiumEventList) {
List<EventTeaserModel> findDuplicatedEvent = events.stream()
.filter(event -> event.getResource().getPath().equals(premiumEvent.getResource().getPath()))
.collect(Collectors.toList());
events.removeAll(findDuplicatedEvent);
}
}
Спасибо @Chaosfire @Thomas @marstran Мне удалось улучшить свой метод:
private void removePremiumEventsFromEvents() {
final Set<String> paths = premiumEventList.stream()
.map(EventTeaserModel::getResource)
.map(Resource::getPath)
.collect(Collectors.toSet());
List<EventTeaserModel> duplicatedEvents = events.stream()
.filter(event -> paths.contains(event.getResource().getPath()))
.collect(Collectors.toList());
events.removeAll(duplicatedEvents);
}
Похоже, это можно упростить, используя removeIf() с тем же предикатом, который вы сейчас используете для фильтрации.
Спасибо @Томас. Это два разных списка событий. Я пытаюсь удалить объекты событий с тем же путем.
Спасибо @Chaosfire, благодаря вашему комментарию я смог упростить: for (EventTeaserModel premiumEvent : premiumEventList) { events.removeIf(event -> event.getResource().getPath().equals(premiumEvent.getResource().getPath())); }
Вы можете сделать events.removeAll(premiumEventList); removeAll() это метод интерфейса List. Однако вам нужно будет переопределить hashcode() и equals() для вашего класса EventTeaserModel, чтобы он идентифицировал одинаковые объекты так, как вы хотите, чтобы они сравнивались.
Это решение похоже на ваше концептуально, но выглядит немного чище. Вы можете сначала найти все пути в premiumEventList, а затем использовать removeIf на events для каждого из путей. Так:
private void removePremiumEventsFromEvents() {
premiumEventList.stream()
.map(EventTeaserModel::getResource)
.map(Resource::getPath)
.forEach(path -> events.removeIf(event ->
event.getResource().getPath().equals(path)));
}
Однако лично я предпочитаю, чтобы мои списки не изменялись/неизменялись. Поэтому я бы вместо этого создал новый список events с отфильтрованными элементами. Так:
private void removePremiumEventsFromEvents() {
final Set<String> paths = premiumEventList.stream()
.map(EventTeaserModel::getResource)
.map(Resource::getPath)
.collect(Collectors.toSet());
// Or preferrably, return the new list.
events = events.stream()
.filter(event -> !paths.contains(event.getResource().getPath()))
.collect(Collectors.toList());
}
Оба подхода имеют разные свойства, на которые вы, возможно, захотите указать: 1) в основном это 2 вложенных цикла (поток + removeIf() для каждого элемента), 2) используется промежуточный набор путей, что делает его более эффективным для достаточно большого количество событий. - Кстати, условие фильтра должно быть отменено, не так ли? -> .filter(event -> !paths.contains(event.getResource().getPath()))
Я расширим свой комментарий: если оба списка могут быть большими, сложность O (n * m) может стать проблематичной. Таким образом, использование промежуточного набора может помочь:
private void removePremiumEventsFromEvents(List<EventTeaserModel> events, List<EventTeaserModel> premiumEvents) {
//Build the map of premium event paths - O(n)
Set<String> pePaths = premiumEvents.stream()
.map(event -> event.getResource().getPath())
.collect(Collectors.toSet());
//this is an in-place removal and thus we can't use a stream here
//if you'd want to create a copy of the list you could use:
// events.stream().filter(e -> !pePaths.contains(e.getResource().getPath())).toList()
Iterator itr = events.iterator();
while(itr.hasNext()) {
//Iterate over events - O(m)
EventTeaserModel event = itr.next();
//check if path is premium - O(log(n))
if ( pePaths.contains(event.getResource().getPath()) {
itr.remove();
}
}
//Overall complexity: O(n + m * log(n)) -> O(m * log(n))
}
Отличный ответ @Thomas и прокомментированный код. Спасибо. Я очень ценю ваши сложные комментарии.
Это может лучше подходить для codereview.stackexchange.com, но в целом это будет сложность O (m * n), поскольку вы перебираете другой список для каждого элемента первого. Сначала создайте набор и получите к нему доступ, что уменьшит это до O (m * log (n)). Но почему бы не хранить события в наборе? Могут ли списки содержать дубликаты? Имеет ли значение порядок? (Примечание: LinkedHashSet дает вам набор свойств с итерацией порядка вставки). - Если события уникальны, но считаются дубликатами в зависимости от их пути, может помочь использование карты path->event вместо набора.