Compare two arrays with not the same order - java

I'm new in coding and I decided to learn java, groovy. I am making a simple exercise. I got a two array and I must compare them if they are equal. I take values from 2 database and these databases are same, but values are not in the same order, but they are equal. For example, I have:
ArrayList collection1 = ["test","a"]
ArrayList collection2 = ["a","test"]
Well I tried this:
assert collection1.equals(collection2)
But I know that this works only when values in those arrays are placed in same order.

I can think of two methods:
Check that they are equal sizes
Wrap the two arrays with Arrays.asList()
Check if a contains all elements from b
public static boolean equals(Object[] a, Object[] b) {
return a.length == b.length && Array.asList(a).containsAll(Arrays.asList(b));
}
Another way would be to just iterate over both arrays at once and then check if the elements are equal:
public static boolean equals(Object[] a, Object[] b) {
if(a.length != b.length) return false;
outer: for(Object aObject : a) {
for(Object bObject : b) {
if(a.equals(b)) continue outer;
}
return false;
}
return true;
}
Both methods are rather fast, the first introduces an additional wrapper around the arrays, but is neglectable as Arrays.asList() just uses the given array as a View and does not do any additional copying.
Now it seems that you're actually comparing two Collections, then you can just use this approach:
public static boolean equals(Collection<?> a, Collection<?> b) {
return a.size() == b.size() && a.containsAll(b);
}

In an array the order is important. If you want an array without checking the order, you should use Sets Sets tutorial.
However, if you don't want to use another type I suggest you implement your own function that checks the presence of each element in one another.
I hope this can helps !

I know absolutely zilch about Java programming, but I've thought of this problem more generally for some time and I think I have a workable solution that is generalizable if you know a priori all the values that can be contained in the array.
If you assign a prime number to each possible string that can be in the array and then multiply all the elements of an array together, then the multiplied number will represent a unique combination, but not order, or the elements of the array. To close the loop, then you just have to compare the values of that multiplication. If there's a better answer, use that, but I thought I would share this idea.

In Groovy, just sort them, and check the sorted lists:
assert listA.sort(false) == listB.sort(false)
Or if they can't have duplicates, use Sets as suggested by #Baldwin

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.

Comparing and creating arrays without having to individually add elements to an array

I'm trying to make it so that if an array is equal to another array then do something.
Something like this:
ArrayList<String> array = new ArrayList<String>();
gridArray.add(string1);
gridArray.add(string2);
gridArray.add(string3);
gridArray.add(string4);
if (array == {a,b,c,d}) {
....
}
if (array == {e,f,g,h}) {
....
}
string1 etc are all variables so will be different each time I run this bit of code. The main thing I want to achieve is to not have to add a,b,c,d etc individually to an array (like for 'array') because I am going to have a lot of the if statements with different values for a,b,c,d, and a lot more values than just a,b,c,d.
This will be more verbose if you don't want to create arrays. The simple way would be:
if (array.equals(Arrays.asList(a, b, c, d)) {}
This will return true if your arraylist contains exactly a, b, c and d (in the same order):
Two lists are defined to be equal if they contain the same elements in the same order.
If you don't care about the order, you can also use containsAll but it will require an extra step:
List<String> target = Arrays.asList(a, b, c, d);
if (array.containsAll(target) && array.size() == target.size()) {}
Arrays have static equals() methods you can use for this:
String[] array = {"a", "b", "c", "d"};
if (Arrays.equals(array, new String[]{"a","b","c","d"})) {
System.out.println("Equal!");
}
Or you can use Apache Common's ArrayUtils compare feature:
isEquals
public static boolean isEquals(java.lang.Object array1,
java.lang.Object array2)
Compares two arrays, using equals(), handling multi-dimensional arrays correctly.

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

Java: How to be sure to store unique arrays based on its values on a List

I have a number of one dimension arrays of Object[] (these objects are primitive types if it helps)
I want to store these arrays in a List, but only the arrays whose contents are unique from the rest.
My first aproximation was to iterate througth the arrays storing in a Set the value of Arrays.hashCode(array) and only storing the array in the desired list if the value was not cointained in the set.
But later i realize that two arrays with different contents can produce the same hashcode (not frequently i hope)
can anyone help?
can i expect very frecuently hashcode collisions (same hascode from different contents)?
It sounds like you need a LinkedHashSet (preserving insertion order while maintaining uniqueness) and then wrap your arrays in an object that implements hashcode and equal in a way that makes sense for your arrays. A first approximation may just be the Arrays.asList() method, but you state in your question that you are using primitives in an Object[] array. Either you are relying on autoboxing, or you are in fact not using an Object[] array but rather an int[], long[], float[] as needed. The Arrays.asList() won't work correctly with those types.
Edit: Per the request of the comment, here is code for a wrapper class:
public class ArrayWrapper {
private Object[]array;
public ArrayWrapper(Object[] array) { this.array = array; }
public Object[] getArray() {
Object[] newArray=new Object[array.length];
System.arraycopy(array,0,newArray,0,array.length);
return newArray;
}
public int hashCode() { return Arrays.hashCode(array); }
public boolean equals(Object obj) {
boolean b=false;
if(obj instanceof ArrayWrapper){
b=Arrays.equals(this.array,((ArrayWrapper)obj).getArray());
}
return b;
}
}
Is the problem that you'll have arrayX and arrayY, both with contents [a,b,c] but the Set doesn't treat them as equal? Would [a,b,c] and [c,a,b] be considered equal, or not?
I'd say define a comparator that defines "equality" for arrays exactly how you need it defined, and then insert each array into a Set that uses the custom comparator that you created.
If the hash code is the same, then you simply further check its detail.
Try something like this:
EDIT
Running and working code below:
bash-3.2$ cat ArraysTest.java
import java.util.*;
public class ArraysTest {
public static void main( String [] args ) {
Set<Integer[]> set = new TreeSet<Integer[]>( new Comparator<Integer[]>() {
public int compare( Integer[] one, Integer[] two ) {
if( Arrays.equals( one, two ) ) {
return 0;
}
return Arrays.hashCode( one ) - Arrays.hashCode( two );
}
public boolean equals( Object o ){ return false; }
});
set.add( new Integer[]{1,2,3});
set.add( new Integer[]{1,2,3});
set.add( new Integer[]{3,2,1});
for( Integer[] i : set ) {
System.out.println( Arrays.asList( i ) );
}
}
}
bash-3.2$ javac ArraysTest.java
bash-3.2$ java ArraysTest
[1, 2, 3]
[3, 2, 1]
bash-3.2$
You'll have to work a bit for make it work, this is just a sample, not actual running code.
As you know the Set only accept one element, and creating the TreeSet with a custom comparator allow you do tell the set what is equals for you.
Arrays.equals() methods describes:
..two arrays are equal if they contain the same elements in the same order...
Following assumes that you consider arrays {1,2,3} and {3,2,1} not to be duplicates.
Do not store hashcode of arrays to the Set, store whole lists to to Set.
Convert your arrays to Lists. Lists have consistent equals and hashCode methods. Two lists are defined to be equal if they contain the same elements in the same order, and the List's hashCode will be consistent with equals method.
List<Object> list = Arrays.asList(array);
Here is the whole algorithm. (Untested code but should work).
Set<List<Object>> findUniqueLists(List<List<Object>> allLists) {
Set<List<Object>> uniqueSet = new LinkedHashSet<List<Object>>();
uniqueSet.addAll(allLists);
Set<List<Object>> processedSet = new LinkedHashSet<List<Object>>();
for(List<Object> list : allLists) {
if(processedSet.contains(list)) {
// duplicate found!
uniqueSet.remove(list);
} else {
// no duplicate
processedSet.add(list)
}
}
return uniqueSet;
}
For comparing efficiently, one uses sometimes a two steps approach:
hashCode discards many potential matches
if two hashCode are equals, the objects themselves are tested for equality (depends on their method equals)
About your Object[] being of primitive types, please remember the following:
To add a primitive type into the Object[], it will always be boxed/unboxed.
So you don't really have primitive types as the contents of your arrays.
To keep the primitive types, the arrays themselves have to be of primitive types, such as int[].

Categories

Resources