Итерация и фильтрация двух списков с помощью java 8

Я хочу перебрать два списка и получить новый отфильтрованный список, который будет иметь значения, отсутствующие во втором списке. Может ли кто-нибудь помочь?

У меня есть два списка: один - список строк, а другой - список объектов MyClass.

List<String> list1;
List<MyClass> list2;

MyClass {

    MyClass(String val)
    {
        this.str = val;
    }

     String str;
     ...
     ...
}

Я хочу отфильтрованный список строк на основе → проверить второй список для элементов (abc), значения которых отсутствуют в list1.

List<String> list1 = Arrays.asList("abc", "xyz", "lmn");

List<MyClass> list2 = new ArrayList<MyClass>();

MyClass obj = new MyClass("abc");
list2.add(obj);
obj = new MyClass("xyz");
list2.add(obj);

Теперь мне нужен новый отфильтрованный список → который будет иметь value = > "lmn". то есть значения, отсутствующие в list2, элементы которых находятся в list1.

Ответ 1

Наконец, я получил способ добиться этого следующим образом -

List<String> unavailable = list1.stream()
                .filter(e -> (list2.stream()
                        .filter(d -> d.getStr().equals(e))
                        .count())<1)
                        .collect(Collectors.toList());

Но это также работает так, как ожидалось. Пожалуйста, дайте мне знать, насколько это эффективно? и если у кого есть другой способ сделать то же самое?

Ответ 2

// produce the filter set by streaming the items from list 2
// assume list2 has elements of type MyClass where getStr gets the
// string that might appear in list1
Set<String> unavailableItems = list2.stream()
    .map(MyClass::getStr)
    .collect(Collectors.toSet());

// stream the list and use the set to filter it
List<String> unavailable = list1.stream()
            .filter(e -> unavailableItems.contains(e))
            .collect(Collectors.toList());

Ответ 3

Выполнение этого с потоками легко и доступно:

Predicate<String> notIn2 = s -> ! list2.stream().anyMatch(mc -> s.equals(mc.str));
List<String> list3 = list1.stream().filter(notIn2).collect(Collectors.toList());

Ответ 4

Если вы передаете первый список и используете фильтр на основе содержит в течение второго...

list1.stream()
    .filter(item -> !list2.contains(item))

Следующий вопрос - какой код вы добавите в конец этой потоковой операции для дальнейшей обработки результатов... вам.

Кроме того, list.contains довольно медленный, поэтому вам будет лучше с наборами.

Но если вы используете наборы, вы можете найти несколько более простых операций для этого, например removeAll

Set list1 = ...;
Set list2 = ...;
Set target = new Set();
target.addAll(list1);
target.removeAll(list2);

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

Ответ 5

Простой подход является foreach в списке1 и проверяет, находится ли элемент в списке2, если не добавить в list3.

outer:
for(String s : list1) {
    for(MyClass c : list2) 
        if(c.getStr().equals(s))
            continue outer;
    filteredList.add(c);
}

Если вы обнаружите, что все еще не сбиваете с толку, извлеките внутренний цикл в функцию логического возврата. Вы также можете заменить классический foreach для итератора потока лямбда.

public static boolean isInList(ArrayList<MyClass> list, String s) {
    list2.stream().foreach((o)-> {
        if(o.getStr().equals(s)) {
            return true;
        }
    });
    return false;
}

list1.stream().foreach((s) -> {
    if(!isInList(list2, s)) {
        list3.add(s);
    }
});

но он действительно выглядит более неприглядным/загрязненным и ненужным для моих глаз.

Кроме того, String str в вашем классе не имеет открытого определения, поэтому я в обоих примерах использовал метод getStr(), предполагая, что ваш класс следует модели java bean и содержит getStr() метод.

Ответ 6

См. ниже, будет приветствовать обратную связь по приведенному ниже коду.

не существует между двумя массивами:

List<String> l3 =list1.stream().filter(x -> !list2.contains(x)).collect(Collectors.toList());

Общее между двумя массивами:

List<String> l3 =list1.stream().filter(x -> list2.contains(x)).collect(Collectors.toList());

Ответ 7

list1 = list1.stream().filter(str1-> 
        list2.stream().map(x->x.getStr()).collect(Collectors.toSet())
        .contains(str1)).collect(Collectors.toList());

Это может работать более эффективно.

Ответ 8

если у вас есть класс с идентификатором, и вы хотите фильтровать по идентификатору

line1: вы распечатываете все идентификаторы

line2: отфильтруйте то, что не существует на карте

Set<String> mapId = entityResponse.getEntities().stream().map(Entity::getId).collect(Collectors.toSet());

List<String> entityNotExist = entityValues.stream().filter(n -> !mapId.contains(n.getId())).map(DTOEntity::getId).collect(Collectors.toList());

Ответ 9

Ответ @DSchmdit работал на меня. Я хотел бы добавить к этому. Поэтому мое требование состояло в том, чтобы отфильтровать файл на основе некоторых конфигураций, хранящихся в таблице. Файл сначала извлекается и собирается как список dtos. Я получаю конфигурации из БД и сохраняю их как другой список. Вот как я заставил фильтрацию работать с потоками

    List<FPRSDeferralModel> modelList = Files
            .lines(Paths.get("src/main/resources/rootFiles/XXXXX.txt")).parallel().parallel()
            .map(line -> {
                FileModel fileModel= new FileModel();
                line = line.trim();
                if (line != null && !line.isEmpty()) {
                    System.out.println("line" + line);
                    fileModel.setPlanId(Long.parseLong(line.substring(0, 5)));
                    fileModel.setDivisionList(line.substring(15, 30));
                    fileModel.setRegionList(line.substring(31, 50));
                    Map<String, String> newMap = new HashedMap<>();
                    newMap.put("other", line.substring(51, 80));
                    fileModel.setOtherDetailsMap(newMap);

                }
                return fileModel;
            }).collect(Collectors.toList());

    for (FileModel model : modelList) {
        System.out.println("model:" + model);
    }

    DbConfigModelList respList = populate();
    System.out.println("after populate");
    List<DbConfig> respModelList = respList.getFeedbackResponseList();


    Predicate<FileModel> somePre = s -> respModelList.stream().anyMatch(respitem -> {

        System.out.println("sinde respitem:"+respitem.getPrimaryConfig().getPlanId());
        System.out.println("s.getPlanid()"+s.getPlanId());
        System.out.println("s.getPlanId() == respitem.getPrimaryConfig().getPlanId():"+
        (s.getPlanId().compareTo(respitem.getPrimaryConfig().getPlanId())));
        return s.getPlanId().compareTo(respitem.getPrimaryConfig().getPlanId()) == 0
                && (s.getSsnId() != null);
    });



 final List<FileModel> finalList =  modelList.stream().parallel().filter(somePre).collect(Collectors.toList());

 finalList.stream().forEach(item -> {
     System.out.println("filtered item is:"+item);
 });

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

Ответ 10

'List<String> unavailable = list1.stream()
                .filter(e -> (list2.stream()
                        .filter(d -> d.getStr().equals(e))
                        .count())<1)
                        .collect(Collectors.toList());'
for this if i change to 
'List<String> unavailable = list1.stream()
                .filter(e -> (list2.stream()
                        .filter(d -> d.getStr().equals(e))
                        .count())>0)
                        .collect(Collectors.toList());'
will it give me list1 matched with list2 right?