Удаление объектов из списка на основе их пути

У меня есть два списка:

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

Это может лучше подходить для codereview.stackexchange.com, но в целом это будет сложность O (m * n), поскольку вы перебираете другой список для каждого элемента первого. Сначала создайте набор и получите к нему доступ, что уменьшит это до O (m * log (n)). Но почему бы не хранить события в наборе? Могут ли списки содержать дубликаты? Имеет ли значение порядок? (Примечание: LinkedHashSet дает вам набор свойств с итерацией порядка вставки). - Если события уникальны, но считаются дубликатами в зависимости от их пути, может помочь использование карты path->event вместо набора.

Thomas 16.02.2023 13:12

Похоже, это можно упростить, используя removeIf() с тем же предикатом, который вы сейчас используете для фильтрации.

Chaosfire 16.02.2023 13:12

Спасибо @Томас. Это два разных списка событий. Я пытаюсь удалить объекты событий с тем же путем.

scura s 16.02.2023 13:22

Спасибо @Chaosfire, благодаря вашему комментарию я смог упростить: for (EventTeaserModel premiumEvent : premiumEventList) { events.removeIf(event -> event.getResource().getPath().equals(premiumEvent.getResourc‌​e().getPath())); }

scura s 16.02.2023 13:27
Как сделать движок для футбольного матча? (простой вариант)
Как сделать движок для футбольного матча? (простой вариант)
Футбол. Для многих людей, живущих на земле, эта игра - больше, чем просто спорт. И эти люди всегда мечтают стать футболистом или менеджером. Но, к...
Знайте свои исключения!
Знайте свои исключения!
В Java исключение - это событие, возникающее во время выполнения программы, которое нарушает нормальный ход выполнения инструкций программы. Когда...
Лучшая компания по разработке спортивных приложений
Лучшая компания по разработке спортивных приложений
Ищете лучшую компанию по разработке спортивных приложений? Этот список, несомненно, облегчит вашу работу!
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Blibli Automation Journey - Как захватить сетевой трафик с помощью утилиты HAR в Selenium 4
Если вы являетесь веб-разработчиком или тестировщиком, вы можете быть знакомы с Selenium, популярным инструментом для автоматизации работы...
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Что такое Java 8 Streams API? Java 8 Stream API
Деревья поиска (Алгоритм4 Заметки к учебнику)
Деревья поиска (Алгоритм4 Заметки к учебнику)
(1) Двоичные деревья поиска: среднее lgN, наихудшее N для вставки и поиска.
0
4
66
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Вы можете сделать 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()))

Thomas 16.02.2023 13:48

Я расширим свой комментарий: если оба списка могут быть большими, сложность 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 и прокомментированный код. Спасибо. Я очень ценю ваши сложные комментарии.

scura s 17.02.2023 09:28

Другие вопросы по теме