Итерация и фильтрация двух списков с помощью 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?