Calculating difference of two lists with duplicates - java

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.

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.

How about:
list2.forEach(i -> {
list1.remove(i); //removes only first occurrence - if found
});
list1 now contains
[1, 2]

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.

Related

how to remove unique elements from arrayList

I have two lists, in one list values are 1,2,3
another list 2,3
I want to remove the values which are not matched in both lists.
2 and 3 are matched in both lists then 1 is not mached in both lists so I want to remove that value.
List original = [1,2,3];
List dummy = [2,3];
If it's possible to use sets instead, then you can just get the intersection between the sets (info):
Set<String> s1;
Set<String> s2;
s1.retainAll(s2); // s1 now contains only elements in both sets
Of course with sets you can't have duplicates and you'll lose ordering.
You don't need to use a Set to achieve your requirement.
Use retainAll() defined in Collection that any List implementation implements such as :
List<Integer> original = new ArrayList<>(Arrays.asList(1,2,3));
List<Integer> dummy = Arrays.asList(2,3);
original.retainAll(dummy);
System.out.println(original);
Output :
[2, 3]
If you are using Java 8+ you can use :
original.removeIf(a -> !dummy.contains(a));
Here is an example with Java 10
var original = new ArrayList<>(List.of(1, 2, 3, 4));
var dummy = new ArrayList<>(List.of(2, 4, 3));
original.removeIf(a -> !dummy.contains(a));
System.out.println(original);
->[2, 3, 4]

ArrayList contains one or more entities from another ArrayList [duplicate]

This question already has answers here:
Something like 'contains any' for Java set?
(9 answers)
Closed 4 years ago.
I have two ArrayLists (list1 & list2). I would like to see if any 1 (or more) of the objects in list2(which are strings) occur in list1.
So, for some examples:
List<String> list1 = Arrays.asList("ABCD", "EFGH", "IJKL", "QWER");
List<String> list2 = Arrays.asList("ABCD", "1234");
//Should result in true, because "ABCD" is in list 1 & 2
However, the method containsAll() does not work in this use case, as 1234 does not occur in list1, and will result in a result of false, also contains() does not work.
Besides writing my own implementation(comparing all of the values individually from list2 to list1), is there a similar method to contains(), where a list of strings can be passed in, and compare that to another list, and return true if one or more values are contained in that list?
More Examples:
ArrayList list1 = {1, 2, 3, 4, 5}
ArrayList list2 = {1, 2, 3} --> True
ArrayList list2 = {3, 2, 1} --> True
ArrayList list2 = {5, 6, 7, 8, 9} --> True
ArrayList list2 = {6, 7, 8, 9} --> False
Like list2.stream().anyMatch(list1::contains) in java 8.
Pre-Java 8, you can use retainAll to achieve this quite simply:
Set<Foo> intersect = new HashSet<>(listOne);
intersect.retainAll(listTwo);
return !intersect.isEmpty();
There's no need to use streams for this task. Use the Collections.disjoint method instead:
boolean result = !Collections.disjoint(list1, list2);
According to the docs:
Returns true if the two specified collections have no elements in common.
list2.removeAll(list1);
If you just need return boolean. Use removeAll(), it the length of list2 shrink, any item(s) in common would be removed.

Check If a particular list is present in "List of Lists" irrespective of order

I have a list of lists like
[[0, 5, 10, -1], [1, 8, 3, 4], [2, 9, 6, 7]]
Now I want to search If the above list of lists contain
[8,1,4,3]
I simply want to ignore the order of elements.
How can I achieve this.
** I tried contains but it fails when the order is different.
Edit:
The code I'm using
for(List<Integer> innerSList : PermutaionS_toDraw)
{int elem_trans = 0;
if(!Final.contains(innerSList))
{
Final.add(PermutaionS_toDraw.get(PermutaionS_toDraw.indexOf(innerSList)));
}
}
Iterate over all the lists, for each list check they are the same length as the one you are looking to compare and if so call containsAll to check it contains all the values.
for(List<Integer> innerList : permutaionsToDraw){
int elemTrans = 0;
if(final.size() != innerList.size() || !final.containsAll(innerSList)) {
final.add(permutaionsToDraw.get(permutaionsToDraw.indexOf(innerList)));
}
}
Note
I renamed your variables to keep with Java naming conventions
As you stated, you'll want to do a contains, however I think you're doing it at the wrong level. Instead of seeing if the main list contains your sublist, you'd want to iterate through the main list and check if each of it's values contains all of your search value. For example,
for (List<String> subList : mainList) {
if (subList.containsAll(searchList) {
return true;
}
}
return false;
This will loop through your main list and check if each of it's children contains all the values of what you're searching for. Hope this helps and that I've explained it clearly!
Note: This depends on the type of collections you are using. A standard String[] won't work with this code
Edit: To react to your posting of the code. I believe all you'd need to change is the contains to a contains all. Contains will only look for an exact match within Final, however contains all will compare the contents and check if that collection of integers is contained at all within Final. So, it should look something like this
List<List<Integer>> finalCopy = new LinkedList<>();
for(List<Integer> innerSList : PermutaionS_toDraw){
for (List<Integer> subList : Final) {
if(!subList.containsAll(innerSList)) {
finalCopy.add(innerSList);
}
}
}
Final.addAll(finalCopy);
This should do the trick. for every sublist this snippet checks if the searchlist is the same size, contains the same elements (in any order) and is not already in the Final list of lists.
for(List<Integer> innerSList : PermutaionS_toDraw)
{
if(searchlist.size() == innerSList.size() && searchList.containsAll(innerSList)
&& !Final.contains(searchList))
{
Final.add(PermutaionS_toDraw.get(PermutaionS_toDraw.indexOf(innerSList)));
}
}
This can be done by using .ContainsAll() method on any Implementing classes of List Interface (ArrayList in my example). The parameter of ContainsAll() takes a Collection and ContainsAll() returns a boolean value.
Please see : List
If you are already working with arrays you can convert them using Arrays.asList()
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Test {
public static void main(String[] args) {
List<Integer> integersList = new ArrayList<>();
integersList.add(1);
integersList.add(2);
integersList.add(3);
integersList.add(4);
List<Integer> integersList2 = new ArrayList<>();
integersList2.add(5);
integersList2.add(6);
integersList2.add(7);
integersList2.add(8);
List<Integer> integersList3 = new ArrayList<>();
integersList3.add(1);
integersList3.add(2);
integersList3.add(3);
integersList3.add(4);
List<List> listOfLists = new ArrayList<>();
listOfLists.add(integersList);
listOfLists.add(integersList2);
listOfLists.add(integersList3);
List<Integer> numbersToLookFor = new ArrayList<>();
numbersToLookFor.add(1);
numbersToLookFor.add(2);
numbersToLookFor.add(3);
numbersToLookFor.add(4);
for (List l : listOfLists) {
if (l.containsAll(numbersToLookFor)) {
System.out.println(l + "does contain " + numbersToLookFor);
}
}
}
}
Output:
[1, 2, 3, 4] does contain [1, 2, 3, 4]
[5, 6, 7, 8] does NOT contain [1, 2, 3, 4]
[1, 2, 3, 4] does contain [1, 2, 3, 4]
Edit: if you were to change the numbers to look for to a different order it will still return true.
//CHANGED from 1 2 3 4 --> 3 1 4 2
List<Integer> numbersToLookFor = new ArrayList<>();
numbersToLookFor.add(3);
numbersToLookFor.add(1);
numbersToLookFor.add(4);
numbersToLookFor.add(2);
for (List l : listOfLists) {
if (l.containsAll(numbersToLookFor)) {
System.out.println(l + " does contain " + numbersToLookFor);
} else
System.out.println(l + " does NOT contain " + numbersToLookFor);
}
}
Output:
[1, 2, 3, 4] does contain [3, 1, 4, 2]
[5, 6, 7, 8] does NOT contain [3, 1, 4, 2]
[1, 2, 3, 4] does contain [3, 1, 4, 2]
If you want to check if the sublist contains elements regardless of order that is the same thing as checking if they have the same number of each element, or their sorted lists are equal. So what you want to do is either count each number in your list and add it to a HashMap, or compare the two lists in sorted order

Java 8 streams adding values from two or more lists

I am trying to get into Java 8 and get my head around streams and lambdas to solve various problems and got stuck on this specific one which I normally use a forEach and store the values in a Map to solve.
How would you write the code to get the expected list using the new features in Java 8 ?
List<Integer> voterA = Arrays.asList(1,2,3,4,5);
List<Integer> voterB = Arrays.asList(1,2,3,4,5);
List<List<Integer>> votes = Arrays.asList(voterA, voterB);
// expected list = (2,4,6,8,10)
List<Integer> sumVotes = ...
That one isn't really doable the way you're hoping. The closest you could get would probably be
IntStream.range(0, voterA.size())
.mapToObj(i -> voterA.get(i) + voterB.get(i))
.collect(toList());
...but there's no "zip" operation on streams, largely because two different streams can have backing spliterators that split at different points, so you can't line them up properly.
JDK doesn't provide the 'zip' API. But it can be done with third library abacus-common:
List<Integer> voterA = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> voterB = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> sumVotes = Stream.zip(voterA, voterB, (a, b) -> a + b).toList();
Disclosure: I'm the developer of abacus-common.

fast way to update elements of a list comparing with another list

i got two Lists with integer IDs an old list and a new list.
Now i want to do three steps:
1. Check which IDs from (old)List1 are also found in (new)List2
2. Delete all elements from List1 that are not found in List2 after step 1
3. Add all missing IDs from List 2 to List 1
I was thinking of adding two boolean arrays and set a flag when an element was found, later i could delete the IDs elements from List1 and add the unchecked IDs from List2 in List1.
Maybe there are better ways?
Try it this way:
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4));
List<Integer> list2 = new ArrayList<>(Arrays.asList(2, 3, 5, 6));
list1.retainAll(list2);
list2.removeAll(list1);
list1.addAll(list2);
System.out.println(list1);
OUTPUT:
[2, 3, 5, 6]
put all content in a lookup table, for instance use a HashSet.

Categories