Using contains on an ArrayList with integer arrays - java

I have an ArrayList<int[]>, and I add an array to it.
ArrayList<int[]> j = new ArrayList<int[]>();
int[] w = {1,2};
j.add(w);
Suppose I want to know if j contains an array that has {1,2} in it without using w, since I will be calling it from another class. So, I create a new array with {1,2} in it...
int[] t = {1,2};
return j.contains(t);
...but this would return false even though w was added to the list, and w contains the exact same array as t.
Is there a way to use contains such that I can just check to see if one of the elements of the ArrayList has the array value {1,2}?

Arrays can only be compared with Arrays.equals().
You probably want an ArrayList of ArrayLists.
ArrayList<ArrayList<Integer>> j = new ArrayList<ArrayList<Integer>>();
ArrayList<Integer> w = new ArrayList<Integer>();
w.add(1); w.add(2);
j.add(w);
ArrayList<Integer> t = new ArrayList<Integer>();
t.add(1); t.add(2);
return j.contains(t); // should return true.

The problem here is that arrays don't override Object.equals(Object), So the comparison between two list entries happens with the default equals() implementation
// from Object.class
public boolean equals(Object obj) {
return (this == obj);
}
So you have to iterate over the list and check all entries using Arrays.equals(int[], int[]). Here's a Helper method that does this:
public static boolean isInList(
final List<int[]> list, final int[] candidate){
for(final int[] item : list){
if(Arrays.equals(item, candidate)){
return true;
}
}
return false;
}
Update: Ever since Java 8, this has got a lot simpler:
public static boolean isInList(
final List<int[]> list, final int[] candidate) {
return list.stream().anyMatch(a -> Arrays.equals(a, candidate));
// ^-- or you may want to use .parallelStream() here instead
}

You need to iterate through the list and manually check whether an array matches your criteria.
public static boolean containsSubArray(List<int[]> j, int[] sub) {
for ( int[] arr : j ) {
if (arr contains elements of sub) {
return true;
}
}
return false;
}
If you want an exact match, you can make use of Arrays.equals(). I don't think there's a library function to do a contains all on an array though, so you would have to write that yourself if that's what you wanted.

"contains" contract checks for equality. So in your case what is failing is equality of int[]. Since Array does not override the equals method from Object you will need a workaround to check for containment.
If you need to check for containment within the Array then you are left with no choice but to iterate through the ArrayList and do the comparison yourself.

from java api:
public boolean contains(Object o)
Returns true if this list contains the
specified element. More formally,
returns true if and only if this list
contains at least one element e such
that (o==null ? e==null : o.equals(e)).
since int[] is a primitive, im pretty sure no .equals method exists so its my guess it would always return false.
I would recommend a different way of storing the data? maybe with a key of some sort?

Two java array arrays are equal iff they have the same object reference. Content doesn't matter.
You're looking for a way to check if they have an equal content. This could help:
Arrays.equals(new int[]{1,2}, new int[]{1,2}); // evaluates to true
Arrays.equals(new int[]{1,2}, new int[]{2,1}); // evaluates to false (!)
If order shouldn't affect equality, then you will have to implement a static equals method by yourself.

First they are not the same Object reference, so they are not equal. equals() will return false.
For your condition, you will need to implement a method to compare them yourself.

If you have one array and want to compare that all elements of array are present in list:
Long[] array1 = {1111L, 1112L};
Long[] array2 = {1111L, 1114L};
List<Long> list = new ArrayList<>();
list.add(1111L);
list.add(1112L);
list.add(1113L);
Arrays.asList(array1).stream().allMatch(val -> list.contains(val)); //return true
Arrays.asList(array2).stream().allMatch(val -> list.contains(val)); //return false

Related

Linear scan for comparing two arrays in Java

I am currently attempting to write an algorithm that can scan two arrays for equality in O(N) time, using Java. The idea is that the arrays should be exactly equal, regardless of order, for the method to return true. What was recommended to me was to use linear scan, but use one loop to scan and compare two arrays. This is what I have:
public boolean equals(ArraySet<T> s) {
T[] sArray = s.getArray();
boolean isEqual = true;
if (!(s.size() == size())) {
return false;
}
int i = 0;
int p = 0;
while ((i < elements.length) && (p < sArray.length)) {
if (elements[i].compareTo(sArray[p]) != 0) {
isEqual = false;
p++;
continue;
}
i++;
p = 0;
isEqual = true;
}
return isEqual;
I'm confident that this algorithm will return the equality correctly, but I'm not so sure that it will do so in O(N) time complexity. Is there anything I could tweak to ensure that this method functions with the proper efficiency? ArraySet is a Set implementation that contains an array field, returned by getArray(). This array and the local array can be assumed to already be in ascending natural order, however the parameter array and the local array can obviously not be assumed to have the same elements.
First of all, what you are doing in your while loop isn't correct.
Scenario 1: Even if we assume both array's are sorted, in your while loop you are incrementing only p and not i. That is if you have two arrays: element={1,2,3} sArray={1,2,3}; you are just comparing element[0] with rest of sArray.
Scenario 2:Let's say both arrays equal in length but are not in sorted order.
element={1,2,3} sArray={2,1,3}; Even here your code again just compares element[0] with rest of the sArray.
A better approach is to use a HashSet and compare your element array to hashset.
Here is a simple example to compare arrays:[we are assuming there are not duplicates in the both arrays, else this solution wont work]
Set<Integer> uniqueList = new HashSet<Integer>();
int[] elements={1,2,3};
int[] sArray = {3,2,1};
boolean isArrayElementsEqual = false;
for(int i=0;i<sArray.length;i++){
uniqueList.add(sArray[i]);// add one of the arrays complete elements into a set
}
for(int i=0;i<elements.length;i++){
if(uniqueList.contains(elements[i])){//compare another array elements with the set, if its exists them remove it from set..such that by the end of this if both arrays have equal contents, set should be empty
uniqueList.remove(elements[i]);
}
}
if(uniqueList.isEmpty()){//if this list is empty it means both arrays have same contents
isArrayElementsEqual = true;
return isArrayElementsEqual;
}
return isArrayElementsEqual;
P.S: You could modify it based on your requirement

Java: See if ArrayList contains ArrayList with duplicate values

I'm currently trying to create a method that determine if an ArrayList(a2) contains an ArrayList(a1), given that both lists contain duplicate values (containsAll wouldn't work as if an ArrayList contains duplicate values, then it would return true regardless of the quantity of the values)
This is what I have: (I believe it would work however I cannot use .remove within the for loop)
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
Integer a1Size= a1.size();
for (Integer integer2:a2){
for (Integer integer1: a1){
if (integer1==integer2){
a1.remove(integer1);
a2.remove(integer2);
if (a1Size==0){
return true;
}
}
}
}
return false;
}
Thanks for the help.
Updated
I think the clearest statement of your question is in one of your comments:
Yes, the example " Example: [dog,cat,cat,bird] is a match for
containing [cat,dog] is false but containing [cat,cat,dog] is true?"
is exactly what I am trying to achieve.
So really, you are not looking for a "subset", because these are not sets. They can contain duplicate elements. What you are really saying is you want to see whether a1 contains all the elements of a2, in the same amounts.
One way to get to that is to count all the elements in both lists. We can get such a count using this method:
private Map<Integer, Integer> getCounter (List<Integer> list) {
Map<Integer, Integer> counter = new HashMap<>();
for (Integer item : list) {
counter.put (item, counter.containsKey(item) ? counter.get(item) + 1 : 1);
}
return counter;
}
We'll rename your method to be called containsAllWithCounts(), and it will use getCounter() as a helper. Your method will also accept List objects as its parameters, rather than ArrayList objects: it's a good practice to specify parameters as interfaces rather than implementations, so you are not tied to using ArrayList types.
With that in mind, we simply scan the counts of the items in a2 and see that they are the same in a1:
public boolean containsAllWithCounts(List<Integer> a1, List<Integer> a2) {
Map<Integer,Integer> counterA1 = getCounter(a1);
Map<Integer,Integer> counterA2 = getCounter(a2);
boolean containsAll = true;
for (Map.Entry<Integer, Integer> entry : counterA2.entrySet ()) {
Integer key = entry.getKey();
Integer count = entry.getValue();
containsAll &= counterA1.containsKey(key) && counterA1.get(key).equals(count);
if (!containsAll) break;
}
return containsAll;
}
If you like, I can rewrite this code to handle arbitrary types, not just Integer objects, using Java generics. Also, all the code can be shortened using Java 8 streams (which I originally used - see comments below). Just let me know in comments.
if you want remove elements from list you have 2 choices:
iterate over copy
use concurrent list implementation
see also:
http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedList-java.util.List-
btw why you don't override contains method ??
here you use simple Object like "Integer" what about when you will be using List< SomeComplexClass > ??
example remove with iterator over copy:
List<Integer> list1 = new ArrayList<Integer>();
List<Integer> list2 = new ArrayList<Integer>();
List<Integer> listCopy = new ArrayList<>(list1);
Iterator<Integer> iterator1 = listCopy.iterator();
while(iterator1.hasNext()) {
Integer next1 = iterator1.next();
Iterator<Integer> iterator2 = list2.iterator();
while (iterator2.hasNext()) {
Integer next2 = iterator2.next();
if(next1.equals(next2)) list1.remove(next1);
}
}
see also this answer about iterator:
Concurrent Modification exception
also don't use == operator to compare objects :) instead use equal method
about use of removeAll() and other similarly methods:
keep in mind that many classes that implements list interface don't override all methods from list interface - so you can end up with unsupported operation exception - thus I prefer "low level" binary/linear/mixed search in this case.
and for comparison of complex classes objects you will need override equal and hashCode methods
f you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.
- Olga
In Java, HashMap works by using hashCode to locate a bucket. Each bucket is a list of items residing in that bucket. The items are scanned, using equals for comparison. When adding items, the HashMap is resized once a certain load percentage is reached.
So, sometimes it will have to compare against a few items, but generally it's much closer to O(1) than O(n).
in short - there is no need to use more resources (memory) and "harness" unnecessary classes - as hash map "get" method gets very expensive as count of item grows.
hashCode -> put to bucket [if many item in bucket] -> get = linear scan
so what counts in removing items ?
complexity of equals and hasCode and used of proper algorithm to iterate
I know this is maybe amature-ish, but...
There is no need to remove the items from both lists, so, just take it from the one list
public boolean isSubset(ArrayList<Integer> a1, ArrayList<Integer> a2) {
for(Integer a1Int : a1){
for (int i = 0; i<a2.size();i++) {
if (a2.get(i).equals(a1Int)) {
a2.remove(i);
break;
}
}
if (a2.size()== 0) {
return true;
}
}
return false;
}
If you want to remove the duplicate values, simply put the arraylist(s) into a HashSet. It will remove the duplicates based on equals() of your object.

How can I compare two array lists for equality with a custom comparator?

To be specific, I have two lists:
List<SystemUserWithNameAndId> list1;
List<SystemUserWithNameAndId> list2;
I want to check if they contain the same system users and ordering is not an issue. I tried to use a comparator to sort them first and then check if they're equal using the equals() method of lists. But I don't want to override the equals method for SystemUserWithNameAndId and I was wondering if I could use the comparator I created for sorting or a similar one to check for equality without explicitly iterating through the lists after sorting.
Comparator<SystemUserWithNameAndId> systemUserComparator = new Comparator<SystemUserWithNameAndId>()
{
#Override
public int compare(SystemUserWithNameAndId systemUser1, SystemUserWithNameAndId systemUser2)
{
final int systemUserId1 = systemUser1.getSystemUserId();
final int systemUserId2 = systemUser2.getSystemUserId();
return systemUserId1 == systemUserId2
? 0
: systemUserId1 - systemUserId2;
}
};
Collections.sort(systemUsers1, systemUserComparator);
Collections.sort(systemUsers2, systemUserComparator);
return systemUsers1.equals(systemUsers2);
Ideally, I want to be able to say,
CollectionUtils.isEqualCollections(systemUsers1, systemUsers2, someCustomComparator);
Just implement the method that iterates, and reuse it every time you need it:
public static <T> boolean areEqualIgnoringOrder(List<T> list1, List<T> list2, Comparator<? super T> comparator) {
// if not the same size, lists are not equal
if (list1.size() != list2.size()) {
return false;
}
// create sorted copies to avoid modifying the original lists
List<T> copy1 = new ArrayList<>(list1);
List<T> copy2 = new ArrayList<>(list2);
Collections.sort(copy1, comparator);
Collections.sort(copy2, comparator);
// iterate through the elements and compare them one by one using
// the provided comparator.
Iterator<T> it1 = copy1.iterator();
Iterator<T> it2 = copy2.iterator();
while (it1.hasNext()) {
T t1 = it1.next();
T t2 = it2.next();
if (comparator.compare(t1, t2) != 0) {
// as soon as a difference is found, stop looping
return false;
}
}
return true;
}
Here's a Java 8 way of solving your problem. First make sure the lists are of equal length:
List<SystemUserWithNameAndId> list1 = ... ;
List<SystemUserWithNameAndId> list2 = ... ;
if (list1.size() != list2.size()) {
return false;
}
Now build a Comparator using the new comparator utilities. The idea is that instead of writing custom logic for a comparator, most comparators do something like comparing two objects by extracting a key from them, and then comparing the keys. That's what this does.
Comparator<SystemUserWithNameAndId> comp =
Comparator.comparingInt(SystemUserWithNameAndId::getSystemUserId);
Sort the lists. Of course, you might want to make copies before sorting if you don't want your function to have the side effect of sorting its input. If your input lists aren't random access (who uses LinkedList nowadays?) you might also want to copy them to ArrayLists to facilitate random access.
list1.sort(comp);
list2.sort(comp);
Run a stream over the indexes of the lists, calling the comparator on each pair. The comparator returns 0 if the elements are equals according to this comparator. If this is true for all pairs of elements, the lists are equal.
return IntStream.range(0, list1.size())
.allMatch(i -> comp.compare(list1.get(i), list2.get(i)) == 0);

Java Check if two lists are mutually exclusive

Ok, I have List a and List b
is there a way to check that no value exist between the two?
List a // 1,2,4,5
List B // 1,6,7,8
Between both list // 1 FAILURE
Collections.disjoint(list1, list2)
returns true if they have no elements in common.
Use Collections.disjoint.
Returns true if the two specified collections have no elements in
common
boolean hasCommonElements = Collections.disjoint(listA, listB);
You can use Collections.disjoint():
public static boolean disjoint(Collection c1, Collection c2): Returns true if the two specified collections have no elements in common.
Code:
List<Integer> a = new ArrayList<Integer>();
List<Integer> b = new ArrayList<Integer>();
System.out.println(Collections.disjoint(a, b));
You have to user Collections.disjoint(a, b);
It returns a boolean: true if the lists have no elements in common.

compare string array using collection

I have two String arrays a,b.
String a [] = {"one","two","three"};
String b [] = {"one","Two","Three","four"};
I need to check whether both arrays are same or not , with case Insensitive .
I know , the following piece of code is perfect for case sensitive.
List <String> l1 = Arrays.asList(a);
List <String> l2 = Arrays.asList(b);
System.out.println(l2.containsAll(l1));
Is there any other way to compare two string array (case Insensitive ) using collection?
Finally , I used TreeSet with case insensitive comparator.
Example :
String [] oldVal = {"one","two","three","Four"};
String [] newVal = {"one","Two","Three","four"};
Set <String> set1 = new TreeSet <String> (String.CASE_INSENSITIVE_ORDER);
Set <String> set2 = new TreeSet <String> (String.CASE_INSENSITIVE_ORDER);
set1.addAll(Arrays.asList(oldVal));
set2.addAll(Arrays.asList(newVal));
System.out.println("--Using Tree Set --- "+ set1.containsAll(set2)); // Return True
Thanks Guys..
Couldn't you just loop it or use some sort of linq (Sorry just noticed this was java you cant use linq...?)
List<string> matches = new List<string>();
bool isSame=true;
foreach(string s1 in l1)
{
foreach(string s2 in l2)
{
if(s1.ToLower() == s2.ToLower())
matches.Add(s1);
else
{
isSame=false;
break;
}
}
if (isSame)
continue;
else
break;
}
if (isSame)
Console.Writeline("They are the same")
else
Console.Writeline("Not the same");
You may want to check the count as I did not add that to the code, for instance l1.count > l2.count (in this case you know whether or not they are the same by the number of elements in the list). Simple test before even looping:
if (l1.Count != l2.Count) {
//don't even bother looping
//display no matches
}
else {
//place rest of code here since l1.count = l2.count
}
CRAP DIDN'T REALIZE THIS WAS FOR JAVA THOUGHT IT WAS FOR C#. APPLY
SAME LOGIC TO JAVA THOUGH...
You could use a TreeMap with a case-insensitive comparator.
If the arrays don't contain duplicates, one way to do this in O(N) is to use a Set that represents a canonical form of the strings in the array. Something like this:
static Set<String> canonicalSet(String[] arr) {
Set<String> upperSet = new HashSet<String>();
for (String s : arr) {
upperSet.add(s.toUpperCase());
}
return upperSet;
}
static boolean equalsCanonically(String[] arr1, String[] arr2) {
return canonicalSet(arr1).equals(canonicalSet(arr2));
}
This is time-optimal.
You can also do variations on this technique to save more space, e.g. instead of constructing the canonical sets and comparing them, you can construct the canonical set for arr1, and then remove entries from that set according to elements of arr2. It the set is empty afterward, and you can always find what you need to remove, the two arrays are canonically equal.
static boolean equalsCanonically2(String[] arr1, String[] arr2) {
Set<String> canon = canonicalSet(arr1);
for (String s : arr2) {
if (!canon.remove(s.toUpperCase())) return false;
}
return canon.isEmpty();
}
You can also do a simple size-comparison check if you think it's worth it (i.e. if often the two arrays don't even have the same number of elements).
If there are duplicates in the arrays, then the Set method will not work as is. You'd need a multiset, and you can either implement your own, or use Google Collections'.
There are also O(N log N) ways to do this involving sorting the strings. You can sort both arrays and then do a simple linear check. A case-insensitive comparator must be used, and in fact it's already there as String.CASE_INSENSITIVE_ORDER.
static boolean equalsCanonically3(String[] arr1, String[] arr2) {
int N = arr1.length;
if (arr2.length != N) return false;
Arrays.sort(arr1, String.CASE_INSENSITIVE_ORDER);
Arrays.sort(arr2, String.CASE_INSENSITIVE_ORDER);
for (int i = 0; i < N; i++) {
if (String.CASE_INSENSITIVE_ORDER.compare(arr1[i], arr2[i]) != 0) {
return false;
}
}
return true;
}
This last technique works even if the arrays contain duplicates. It does it O(N log N). It sorts the arrays passed as parameters, so if the original state is important, you want to pass their clone() instead.
check it in nested loops if you want custom comparison. or if you have large sets of data it might be cheaper to sort arrays first
Your sample data are sorted. If this is guaranteed to be the case in reality, you should do as Andrey says, and use nested loops on the arrays themselves, breaking if/when you find an inequal pair of entries.
If they're not guaranteed to be sorted, I'd dump each of them into a HashSet, and then you can use java's Set containsAll method.
Edit: As Thomman pointed out, containsAll() ultimately relies on equals(). So in order to get the case-insensitive checking your question requests, you have two choices:
1) Upcase or downcase the strings on insertion into the sets. On consideration, I'm not crazy about this method, since not only will you lose duplicate entries, but you'll also fold entries differentiated by case. And so these lists would look to be equal to each other:
String a [] = {"one","one","one", "Two"};
String b [] = {"One", Two"};
2) The other choice is to put your strings into holder objects which override equals(), doing comparison in a case-insensitive way.
You could first check if their lengths are equal. Then you could put items of a in HashMap and go over b and check if the items are there.
Using one for loop -
String [] oldVal = {"one","two","three","Four"};
String [] newVal = {"one","Two","Three","four"};
if(oldVal.length == newVal.length)
{
//
for(int y =0; y<oldVal.length; y++)
{
oldVal[y] = oldVal[y].toUpperCase();
newVal[y] = newVal[y].toUpperCase();
}
return Arrays.asList(oldVal).containsAll(Arrays.asList(newVal));
}
return false;

Categories