Calculating difference of two lists with duplicate

2019-07-15 04:42发布

问题:

I have two lists.

List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 2));
List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 3, 4));

I want to remove the elements contained in list2 from list1, precisely as many times as they are contained in list2. In the example above: when we remove elements in list 1 which exist in list 2, we should get as result [1, 2] (only one occurrence of 2 should be removed from list1 because list2 contains only one instance of 2).

I tried with list1.removeAll(list2); but I got as result list containing only [1].

What is the best way to achieve this? Iterate through both lists simultaneous seems a bit ugly for me.

回答1:

If I understand correctly, you only want to remove a single 2 element from list1 rather than all of them. You can iterate over list2 and attempt to remove each element from list1. Keep in mind that there are more efficient methods than this if list2 cannot contain duplicates.

var list1 = new ArrayList<>(List.of(1, 2, 2));
var list2 = List.of(2, 3, 4);

list2.forEach(list1::remove);    

list1 now contains the following:

[1, 2]

See starman1979's answer for the same solution, but using a lambda rather than a method reference.



回答2:

How about:

list2.forEach(i -> {
    list1.remove(i); //removes only first occurrence - if found
});

list1 now contains

[1, 2]


回答3:

Given

List<Integer> a = new ArrayList<>(Arrays.asList(1, 2, 2));
List<Integer> b = Arrays.asList(2, 3, 4);

Use one of the following variants to get the desired result:

1. Plain java

b.forEach((i)->a.remove(i));

a now contains

[1, 2]

Give credit at original post: add +1:

2. Apache Commons

In apache commons there is a subtract method

Collection<Integer> result = CollectionUtils.subtract(a, b);

result now contains

[1, 2]

Here is how they implemented it

3. Guava

Since guava doesn't offer a subtract method you may find this advice from the google implementors helpful

"create an ArrayList containing a and then call remove on it for each element in b."

Which basically renders to what was already mentioned under 1.