25

I want to iterate two lists and get new filtered list which will have values not present in second list. Can anyone help?

I have two lists - one is list of strings, and the other is list of MyClass objects.

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

MyClass {

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

     String str;
     ...
     ...
}

I want filtered list of strings based on -> check second list for elements (abc) whose values not present in 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);

Now I want new filtered list -> which will have value => "lmn". i.e. values not present in list2 whose elements are in list1.

3
  • Do you want to iterate those two list simultaneously ? Commented Apr 7, 2015 at 11:40
  • Welcome to Stackoverflow! It's good that you are providing some code, but to get an answer you might also try to describe / show what you have tried so far. Commented Apr 7, 2015 at 11:42
  • Yes.. I want to iterate both lists simultaneously. First is superset having list of strings and second is list of objects. Each object in this second list has one string field with which i want to compare first list items. And all those missing elements in second list, I want them to capture in one list. Commented Apr 9, 2015 at 8:12

9 Answers 9

32
// 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());
Sign up to request clarification or add additional context in comments.

3 Comments

Hi Ashley, What keyMapper and ValueMapper toMap() need to pass?
i changed toMap to toSet. But still its not working as expected. unavailable is not getting any elements.
Sorry - that should have been toSet, not toMap - corrected it in the post. Perhaps the logic you're trying to implement isn't quite what I've suggested. Is the idea that you're looking for things which ARE in list1 and list2, or are you trying to find things that ARE in list1 and NOT in list2? If the second, then put a ! in the filter - i.e. .filter(e -> !unavailableItems.contains(e))
13

this can be achieved using below...

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

3 Comments

This way will be very inefficient as the size of list 2 increases above 3. This is because the logic of this is something like for each element in list 1 scan each element of list 2 to find a match add to results if the scan discovers no match So the number of operations is something like list1 size * list 2 size By using a set of list 2 and the contains operation, you'll make this approximately 2x list1 size number of calculations.
Don't looks at the "correct answer" checkmark. This is the same a sort using bubbles! It will have quadratic O time. Better use solutions from stackoverflow.com/a/29534646/907576
This answer helped/worked for me - I have two ArrayLists of objects. These objects have 5 fields in them. I will never know what one of the values is in one of the lists so I cannot use 'contains' - I have to iterate over the second list for every item in the first list and see if 3 of the fields are the same... Most examples of this sort of thing are very contrived - just a list of strings. All that said, I think I prefer my nested for loop since it is easier for me to read and understand.
9

Doing it with streams is easy and readable:

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

1 Comment

there is stream().noneMatch to simplify a bit
5
list1 = list1.stream().filter(str1-> 
        list2.stream().map(x->x.getStr()).collect(Collectors.toSet())
        .contains(str1)).collect(Collectors.toList());

This may work more efficient.

Comments

3

If you stream the first list and use a filter based on contains within the second...

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

The next question is what code you'll add to the end of this streaming operation to further process the results... over to you.

Also, list.contains is quite slow, so you would be better with sets.

But then if you're using sets, you might find some easier operations to handle this, like removeAll

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

Given we don't know how you're going to use this, it's not really possible to advise which approach to take.

1 Comment

You are absolutely right. Contains method works fine. But my problem is, my second list is not plain list of strings. Its list of objects and I want to compare first list with one of properties of object in second list. And I can't do reverse way I.e. Converting first list to set. Because first list is kind of superset. And after comparing both lists, I want new list having elements missing in second list which are present in first.
2

See below, would welcome anyones feedback on the below code.

not common between two arrays:

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

Common between two arrays:

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

Comments

1

if you have class with id and you want to filter by id

line1 : you mape all the id

line2: filter what is not exist in the map

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

1 Comment

While this code snippet may solve the question, including an explanation really helps to improve the quality of your post. Remember that you are answering the question for readers in the future, and those people might not know the reasons for your code suggestion.
1
`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? 

Comments

0

@DSchmdit answer worked for me. I would like to add on that. So my requirement was to filter a file based on some configurations stored in the table. The file is first retrieved and collected as list of dtos. I receive the configurations from the db and store it as another list. This is how I made the filtering work with streams

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

The details are in the implementation of filter predicates. This proves much more perfomant over iterating over loops and filtering out

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.