Behaviour of "containsAll" with duplicates - java

The official documentation (archive) of containsAll only says "Returns true if this list contains all of the elements of the specified collection.". However, I just tested this:
List<Integer> list1 = new ArrayList<>();
list1.add(1);
list1.add(2);
list1.add(1);
List<Integer> list2 = new ArrayList<>();
list2.add(2);
list2.add(1);
list2.add(2);
System.out.println(list1.containsAll(list2));
The result is true, even though list1 does not contain a second 2.
So what is the official, completely defined behaviour of containsAll? Does it act as if all duplicates were removed from both lists? I remember reading somewhere that it can cause problems with duplicates, but I don't know the exact case.

The List.containsAll method behaves just as documented: it returns true if all the elements of the given collection belong to this collection, false otherwise. The docs say nothing about the order or cardinality of the elements.
The documentation for containsAll does not explicitly say how it determines whether an element belongs to the Collection. But the documentation for contains (which is implicitly specifying the semantics of "contains") does: it uses equals. Again, no mention of cardinality.
The containsAll method is declared in the Collection interface and re-declared in the List and Set interfaces, but it's first implemented in the Collection hierarchy by the AbstractCollection class, as follows:
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
As far as I know, this implementation is inherited by most common classes that implement the Collection interface in the Java Collections framework, except for the CopyOnWriteArrayList class and other specialized classes, such as empty lists and checked and immutable wrappers, etc.
So, if you look at the code, you'll see that it fulfils the docs you quoted:
Returns true if this list contains all of the elements of the specified collection.
In the docs of the AbstractList.containsAll method, there's also an #implSpec tag, which says the following:
#implSpec
This implementation iterates over the specified collection, checking each element returned by the iterator in turn to see if it's contained in this collection. If all elements are so contained true is returned, otherwise false.
With regard to possible optimizations, they're all relayed to the different implementations of the contains method, which is also implemented by AbstractCollection in a naive, brute-force-like way. However, contains is overriden in i.e. HashSet to take advantage of hashing, and also in ArrayList, where it uses indexes, etc.

You can iterate over one list and remove elements by value from another, then check if another list size == 0. If it is, then that means all second list elements were present in first list at least as many times as in the second list.
public boolean containsAll(List<Character> source, List<Character> target) {
for (Character character : source) {
target.remove(character);
if (target.isEmpty()) {
return true;
}
}
return target.size() == 0;
}
HashMap will be more efficient if lists are huge
public static boolean containsAll(List<Character> source, List<Character> target) {
Map<Character, Long> targetMap = target.stream().collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
for (Character character : source) {
Long count = targetMap.get(character);
if (count != null) {
if (count > 1) {
targetMap.put(character, --count);
} else {
targetMap.remove(character);
}
}
}
return targetMap.isEmpty();
}

Related

how containsall method from collections interface works in java?

if I have two lists of objects from Collection interface
list 1 = {John, Tim, Tom}
list 2 = {John, Tim}
and both of the lists are instances of ArrayList
how does Java knows if list2 is contained in list1 with list1.containsall(list2)?
I knows that Java uses contain method inside the implementation of containsall() method, and the contain method uses the equal() method. I understand the differences but I am not sure how Java iterates through the elements of list 1.
so If I use list1.containsAll(list2),, constainsAll() method is implemented with a loop that iterates through every object of in this case, list2, and throws false if one of the elements is not in list 1.
So my main question is how does JAVA know that list 1 contains all of the elements without another loop to iterate through the elements of list 1? Does java does the work internally or something?
I currently know that to do such a thing, I would have to use
for (int i = 0; i < list1.size(), i++)
list1.get(i).constainsAll(list2);
,,
that seems more logical to me taking into consideration that I would have to modified the code for containsAll to work correctly and also implement the method of get()
Maybe to answer this formally because I think it is a good Question.
The containsAll method iterates through the provided collection and performs the contains() method on each entry which also iterates through the other list being compared. See below extract from java code
public boolean containsAll(Collection<?> c) {
for (Object e : c)
if (!contains(e))
return false;
return true;
}
and
public boolean contains(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext())
if (it.next()==null)
return true;
} else {
while (it.hasNext())
if (o.equals(it.next()))
return true;
}
return false;
}
This makes this o(n^2) (Worst case scenario if the last values do not match or if the list actually matches) (Which is really bad, especially if you have big collections which you are comparing).
a better approach would be to do something like the following: (Obviously this needs to be adjusted if you are using objects or other collections apart from strings and do some null checks or something)
public boolean containsAllStrings(List<String> list1, List<String> List2) {
Map<String, String> list1Map = list1.stream().collect(Collectors.toMap(c -> c, c -> c));
return List2.stream().allMatch(list1Map::containsKey);
}
This way it Iterates a max number of 2n (one for adding items to map and one for comparing) times (n being the biggest list of the 2) and not n^2.
It may seem the same but hash maps are nice because they contain a pointer to the value in memory (Using the hashed value of the key) and do not iterate overall all the values, making accessing a value in a map always o(1). Which is optimal.
Obviously, there are tradeoffs between approaches like memory utilization, but for speed, this is the best approach.

Converting Array and copying it into a List

To be clear I don't have any problems and don't really need help but I wanted to ask anyway:
Let's say we have a String array
String[] sarr = new String[]{"POTATO", "TOMATO"};
and we have an enum
public enum Food{POTATO, TOMATO, PIZZA}
If I wanted to check if all Strings in sarr are present in Food, I'd do the following:
ArrayList<String> foodstrings = new ArrayList<>();
Arrays.asList(Food.values()).forEach((in) -> foodstrings.add(in.toString()));
if (!foodstrings.containsAll(Arrays.asList(sarr))) doStuff();
Is there a way to do this in less lines of code? Or simply a more elegant way?
You want to determine if all element in your array are contained in the list of food names.
A possible solution is to convert the food names to a Set (to have a O(1) contains); then, we need to determine if all elements in the array are contained in this set:
public static void main(String[] args) {
String[] sarr = new String[]{"POTATO", "TOMATO"};
Set<String> set = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
boolean result = Arrays.stream(sarr).allMatch(set::contains);
}
In your current solution, you are mutating an external variable with forEach, which is a bad practice.
I believe a better version of the first two lines would be:
Set<String> foodstrings = Arrays.stream(Food.values()).map(Enum::name).collect(Collectors.toSet());
Using Set instead of List will improve performance of containsAll, and the code is entirely streamed, instead of using forEach and an external collector.
The if is still good, although you could just combine it all into a single statement (formatted for readability):
if (! Arrays.stream(Food.values())
.map(Enum::name)
.collect(Collectors.toSet())
.containsAll(Arrays.asList(sarr))) {
doStuff();
}
if (Stream.of(sarr).allMatch(s -> Stream.of(Food.values()).anyMatch(t -> s.equals(t.name()))))
{
// all match
}
Create a stream out of sarr (which could be any Collection of objects amenable to the Stream API introduced in Java 1.8)
We ask for the value allMatch, which only returns true if a Predicate (i.e. a function that returns true/false).
For the Predicate expected by allMatch, we provide a lambda that iterates over a second set of objects via a stream, and calls anyMatch: a simple Predicate that will return true if any member object satisfies a provided condition (once again, a boolean function).
We provide yet another lambda to anyMatch which compares members of the 2 collections via their equals implementations.
This solution is semantically equivalent to the invariant
A \subset B
which in our case is
sarr \subset Food.values()
and the following Java < 1.8 code shown below with short-circuiting to mimic the specification (minus the streams overhead):
// assume success, since if both sets are empty the invariant holds
boolean subset = true;
for (String a : sarr)
{
if (null == a) continue;
boolean contained = false;
for (Food b : Food.values())
if (b.name().equals(a)) { contained = true; break; }
if (!contained) { subset = false; break; }
}
if (subset)
{
// all match
}
You could, of course, substitute different collection types and conditions, as well as use parallelStream() to make better use of the hardware available.

Iterating through a TreeSet in java and updating it

I have a TreeSet in Java and I have my own comparator function for this tree set. Now I am traversing this tree set using descendingIterator() method and changing the elements. So does this update the actual tree set as well wrt to the way it is sorted with my custom comparator? Or do I need to remove the element and put back the updated element?
You need to remove the element and add it back. The position of the element in the tree is decided when the element is inserted, by comparing it with other elements. If you change the object so that the comparison to other elements changes, you must remove the element first, then change it, then re-add it.
Note that removing the element while iterating will only work using the iterator's remove method. And you won't be able to add it during the iteration without getting a ConcurrentModificationException, AFAIK. So store it in a list of elements to be re-added to the set once the iteration has ended.
If you modify any part of the object that is a part of the "key" (as defined by your custom comparator) you need to remove and re-insert the object for the tree set to "learn" about the change. You should not be doing it while you are iterating, either: a good approach is to collect items that need changing in one loop, and then modify and re-insert them in another loop.
As a general rule of thumb, it isn't advisable to "modify" any value types added to Java containers which rely on equality, hash code etc. given that none of the known standard containers perform auto-balancing or adjustment in response to the change of values (which makes sense).
Along with Set, this rule is equally valid for Map types. If you are iterating over a map and modify the "key" in-place, things go bad. This is the reason why it is recommended to have immutable types as your Map keys (think of String, Integer etc.) Your case can be demonstrated by a simple example:
public class Test {
public static void main(final String[] args) {
Mutable m1 = new Mutable(1);
Mutable m2 = new Mutable(2);
Mutable m3 = new Mutable(3);
Mutable m4 = new Mutable(4);
TreeSet<Mutable> ts = new TreeSet<Mutable>(new Cmp());
ts.add(m1); ts.add(m2); ts.add(m3); ts.add(m4);
System.out.println(ts);
for (Iterator<Mutable> iter = ts.iterator(); iter.hasNext(); ) {
Mutable m = iter.next();
if (m.i == 1 || m.i == 3) {
m.i = m.i + 10;
}
}
System.out.println(ts);
}
}
class Mutable {
public int i;
public Mutable(int i) {
this.i = i;
}
public String toString() {
return "Mutable[" + i + "]";
}
}
class Cmp implements Comparator<Mutable> {
#Override public int compare(Mutable o1, Mutable o2) {
return Integer.valueOf(o1.i).compareTo(Integer.valueOf(o2.i));
}
}
Output:
[Mutable[1], Mutable[2], Mutable[3], Mutable[4]]
[Mutable[11], Mutable[2], Mutable[13], Mutable[4]]

Comparing two Collections in Java

I have two Collections in a Java class.The first collection contains previous data, the second contains updated data from the previous collection.
I would like to compare the two collections but I'm not sure of the best way to implement this efficiently.Both collections will contain the same amount of items.
Based then on the carType being the same in each collection I want to execute the carType method.
Any help is appreciated
Difficult to help, because you didn't tell us how you like to compare the (equal-size) collections. Some ideas, hoping one will fit:
Compare both collections if they contain the same objects in the same order
Iterator targetIt = target.iterator();
for (Object obj:source)
if (!obj.equals(targetIt.next()))
// compare result -> false
Compare both collections if they contain the same objects in the any order
for (Object obj:source)
if (target.contains(obj))
// compare result -> false
Find elements in other collection that has changed
Iterator targetIt = target.iterator();
for (Object obj:source)
if (!obj.equals(targetIt.next())
// Element has changed
Based on your comment, this algorithm would do it. It collects all Cars that have been updated. If the method result is an empty list, both collections contain equal entries in the same order. The algorithm relies on a correct implementation of equals() on the Car type!
public List<Car> findUpdatedCars(Collection<Car> oldCars, Collection<Car> newCars)
List<Car> updatedCars = new ArrayList<Car>();
Iterator oldIt = oldCars.iterator();
for (Car newCar:newCars) {
if (!newCar.equals(oldIt.next()) {
updatedCars.add(newCar);
}
}
return updatedCars;
}
From the set arithmetics, the sets A and B are equal iff A subsetequal B and B subsetequal A. So, in Java, given two collections A and B you can check their equality without respect to the order of the elements with
boolean collectionsAreEqual = A.containsAll(B) && B.containsAll(A);
Iterate over the first collection and add it into a Map<Entity, Integer> whereby Entity is the class being stored in your collection and the Integer represents the number of times it occurs.
Iterate over the second collection and, for each element attempt to look it up in the Map - If it exists then decrement the Integer value by one and perform any action necessary when a match is found. If the Integer value has reached zero then remove the (Entity, Integer) entry from the map.
This algorithm will run in linear time assuming you've implemented an efficient hashCode() method.
Slightly updated one considering null values:
static <T> boolean equals(Collection<T> lhs, Collection<T> rhs) {
boolean equals = false;
if(lhs!=null && rhs!=null) {
equals = lhs.size( ) == rhs.size( ) && lhs.containsAll(rhs) && rhs.containsAll(lhs);
} else if (lhs==null && rhs==null) {
equals = true;
}
return equals;
}
If not worried about cases like (2,2,3), (2,3,3):
static <T> boolean equals(Collection<T> lhs, Collection<T> rhs) {
return lhs.size( ) == rhs.size( ) && lhs.containsAll(rhs) && rhs.containsAll(lhs);
}
public static boolean isEqualCollection(java.util.Collection a,
java.util.Collection b)
Returns true if the given Collections contain exactly the same elements with exactly the same cardinalities.
That is, iff the cardinality of e in a is equal to the cardinality of e in b, for each element e in a or b.
Parameters:
the first collection, must not be null
the second
collection, must not be null
Returns:
true if the collections contain the same elements with the same cardinalities.

How can I check if two ArrayList differ, I don't care what's changed

How can I check if two ArrayLists differ from one another? I don't care what's the difference, I just want to know if they're not the same.
I'm fetching scores list from a database every minute, and only if the scores list that I fetched is different from the one I fetched a minute ago I want to send it to the client.
Now the value of the ArrayList is actually a class that I created (that contains name, lvl, rank, score).
Do I need to implement equals() on it?
On the definition of "sameness"
As Joachim noted, for most application, List.equals(Object o) definition works:
Compares the specified object with this list for equality. Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal. (Two elements e1 and e2 are equal if (e1==null ? e2==null : e1.equals(e2)).) In other words, two lists are defined to be equal if they contain the same elements in the same order. This definition ensures that the equals method works properly across different implementations of the List interface.
Depending on how you're using it, though, this may not work as expected. If you have a List<int[]>, for example, it doesn't quite work because arrays inherit equals from Object which defines equality as reference identity.
List<int[]> list1 = Arrays.asList(new int[] { 1, 2, 3 });
List<int[]> list2 = Arrays.asList(new int[] { 1, 2, 3 });
System.out.println(list1.equals(list2)); // prints "false"
Also, two lists with different type parameter can be equals:
List<Number> list1 = new ArrayList<Number>();
List<String> list2 = new ArrayList<String>();
System.out.println(list1.equals(list2)); // prints "true"
You also mentioned that the list must contain elements with the same type. Here's yet another example where the elements don't have the same type, and yet they're equals:
List<Object> list1 = new ArrayList<Object>();
List<Object> list2 = new ArrayList<Object>();
list1.add(new ArrayList<Integer>());
list2.add(new LinkedList<String>());
System.out.println(list1.equals(list2)); // prints "true"
So unless you clearly define what equality means to you, the question can have very different answers. For most practical purposes, though, List.equals should suffice.
On implementing equals
Information after update suggests that List.equals will do the job just fine, provided that the elements implement equals properly (because List<E>.equals invokes E.equals on the non-null-elements, per the API documentation above).
So in this case, if we have, say, a List<Player>, then Player must #Override equals(Object o) to return true if o instanceof Player and on the relevant fields, they're all equals (for reference types) or == (for primitives).
Of course, when you #Override equals, you should also #Override int hashCode(). The barely acceptable minimum is to return 42;; slightly better is to return name.hashCode();; best is to use a formula that involves all the fields on which you define equals. A good IDE can automatically generate equals/hashCode methods for you.
See also
Effective Java 2nd Edition
Item 8: Obey the general contract when overriding equals
Item 9: Always override hashcode when you override equals
API links
Object.equals(Object)
Object.hashCode()
java.lang.Comparable - not needed here, but another important Java API contract
Related questions
On equals/hashCode combo:
Overriding equals and hashcode in Java
Why both hashCode() and equals() exist
How to ensure hashCode() is consistent with equals()?
On equals vs ==:
Difference between equals and ==
why equals() method when we have == operator?
Java String.equals versus ==
Use equals(). As long as the elements inside the lists implement equals() correctly it will return the correct values.
Unless you want to ignore the order of the values, then you should dump the values in two Set objects and compare those using equals().
Here's a simple method that checks if 2 Array Lists contain the same values regardless their order.
//the name of the method explains it well...
public boolean isTwoArrayListsWithSameValues(ArrayList<Object> list1, ArrayList<Object> list2)
{
//null checking
if(list1==null && list2==null)
return true;
if((list1 == null && list2 != null) || (list1 != null && list2 == null))
return false;
if(list1.size()!=list2.size())
return false;
for(Object itemList1: list1)
{
if(!list2.contains(itemList1))
return false;
}
return true;
}
As #Joachim Sauer mentioned in his answer, equals should work if the lists are equal and their contents implement equals correctly. But, it shouldn't work if the items are not in the same "order" since it doesn't use contains for the check. In this sense, it checks for "strict" equality as mentioned by #jarnbjo
//From android's Arraylist implementation
Iterator<?> it = that.iterator();
for (int i = 0; i < s; i++) {
Object eThis = a[i];
Object eThat = it.next();
if (eThis == null ? eThat != null : !eThis.equals(eThat)) {
return false;
}
}
However, I wanted somewhat different behaviour, I didn't care about order or anything like that. All I wanted was to be sure the two didn't contain the same items.
My solution,
//first check that both are not null and are of same length. (not shown here)
//if both match, pull out the big guns as below
...
List<Object> comparedList = new ArrayList<>(listOne);
comparedList.removeAll(listTwo);
if(comparedList.size() != 0) //there are differences between the two
This is less performant since it loops twice, first in removeAll and then in contains which is called by removeAll.
My list was guaranteed to be short so I didn't mind the hit.
You can convert them to string and then compare like
list1.toString().equals(list2.toString())
You can also check the Arraylist as shown below:
public boolean equalLists(List<String> one, List<String> two){
if (one == null && two == null){
return true;
}
if((one == null && two != null)
|| one != null && two == null
|| one.size() != two.size()){
return false;
}
//to avoid messing the order of the lists we will use a copy
//as noted in comments by A. R. S.
one = new ArrayList<String>(one);
two = new ArrayList<String>(two);
Collections.sort(one);
Collections.sort(two);
return one.equals(two);
}
Thanks to #Jacob

Categories

Resources