Comparing and removing object from ArrayLists using Java 8

My apologies if this is simple basic information that I need to know. This is the first time I am trying to use Java 8 streams and other features.

I have two ArrayLists containing objects of the same type. Let's say list1

and list2

. Let's say lists have Person objects with the " employeeId " property .

The scenario is that I need to combine these lists. However, it list2

may have some objects the same as in list1

. So I'm trying to remove objects from list2

that are the same as in list1

and get a list of results that can then be concatenated into list1

.

I am trying to do this with Java 8's removeIf () and stream () functions . Below is my code:

public List<PersonDto> removeDuplicates(List<PersonDto> list1, List<PersonDto> list2) {
        List<PersonDto> filteredList = list2.removeIf(list2Obj -> {
            list1.stream()
                .anyMatch( list1Obj -> (list1Obj.getEmployeeId() == list2Obj.getEmployeeId()) );
        } );
    }

      

The above code gives a compilation error as shown below:

The removeIf (Predicate) method in the type collection is not applicable for arguments ((list2Obj) → {})

So, I changed list2Obj

at the beginning " removeIf () " to (<PersonDto> list2Obj)

as shown below:

public List<PersonDto> removeDuplicates(List<PersonDto> list1, List<PersonDto> list2) {
            List<PersonDto> filteredList = list2.removeIf((<PersonDto> list2Obj) -> {
                list1.stream()
                    .anyMatch( list1Obj -> (list1Obj.getEmployeeId() == list2Obj.getEmployeeId()) );
            } );
        }

      

It gives me an error like below:

Syntax error in token "<", remove this token for < 'in (<PersonDto> list2Obj)

and Syntax error on token (s), inappropriate construction (s) for part from → { '

I am at a loss on what I really need to do to make it work.

Thanks if anyone can help me solve this problem.

+3


source to share


2 answers


I've simplified your function a bit to make it more readable:

public static List<PersonDto> removeDuplicates(List<PersonDto> left, List<PersonDto> right) {
    left.removeIf(p -> {
        return right.stream().anyMatch(x -> (p.getEmployeeId() == x.getEmployeeId()));
    });
    return left;
}

      

Also note that you are changing the parameter left

, you are not creating a new one List

.



You can also use:, left.removeAll(right)

but for that you need equals

and hashcode

, and you don't seem to have them; or are they based on something else than employeeId

.

Another option is to collect these lists in TreeSet

and use removeAll

:

TreeSet<PersonDto> leftTree = left.stream()
      .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(PersonDto::getEmployeeId))));

TreeSet<PersonDto> rightTree = right.stream()
      .collect(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(PersonDto::getEmployeeId))));

leftTree.removeAll(rightTree);

      

+2


source


I understand that you are trying to combine both lists without duplicating the elements belonging to the intersection. There are many ways to do this. One is the way you tried, i.e. remove items from one list that belong to another, then merge. And this, in turn, can be done in several ways.

One such way would be to store the employee IDs of one list in HashSet

, and then use it removeIf

in another list with a predicate that checks if each item has an employee ID that is contained in the set. This is better than using anyMatch

the second list for each element of the first list because it HashSet.contains

works in O(1)

amortized time. Here's a sketch of the solution:

// Determine larger and smaller lists
boolean list1Smaller = list1.size() < list2.size();
List<PersonDto> smallerList = list1Smaller ? list1 : list2;
List<PersonDto> largerList = list1Smaller ? list2 : list1;

// Create a Set with the employee ids of the larger list
// Assuming employee ids are long
Set<Long> largerSet = largerList.stream()
    .map(PersonDto::getEmployeeId)
    .collect(Collectors.toSet());

// Now remove elements from the smaller list
smallerList.removeIf(dto -> largerSet.contains(dto.getEmployeeId()));

      



The logic behind this is that it HashSet.contains

will take the same time for both large and small set, since it runs in O(1)

amortized time. However, traversing the list and removing items from it will be faster on smaller lists.

Then you're ready to combine both lists:

largerList.addAll(smallerList);

      

+2


source







All Articles