compare string array using collection - java

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;

Related

How to return an array of non duplicate strings without using Streams or Hashsets

I wanted to create a function which has the following protoype:
public static String[] removeDuplicates(String [] strings)
This function is supposed to take an array of strings and return another array of strings without duplicate strings. I am trying not to use Streams or Hashset as this is supposed to be a not so advanced exercise and I should only use Arrays. I would also like to do this allocating as little memory as possible, not creating many copies of the array.
The obvious solution which comes to mind is having a copy of the array where I'll add strings to it if they're not already there. However, that involves iterating over an array to see if it's already there and also creating a new copy to return because I probably allocated more space than was occupied on the array. Is there a better way to do this?
If you aren't allowed streams, or sets or other auxiliary data structures, then the "obvious" solution is pretty much all you can do; but sorting the array first will make it more efficient, since then duplicates will always appear next to each other.
Note that you'll need to do two passes, first to count the distinct elements, and then to put the distinct elements into a result array. You won't be able to allocate the result array before knowing how long it should be.
How to return an array of non duplicate strings without using Streams
or Hashsets
You can use an ArrayList for this purpose. While iterating the array of strings, simply check if the string is already present in the list; if not, add it to the list. Finally, convert the list to an array and return the same.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main {
public static void main(String[] args) {
// Test
System.out.println(Arrays.toString(removeDuplicates(new String[] { "Hello", "Hi", "Bye", "Hello", "Hi" })));
}
public static String[] removeDuplicates(String[] strings) {
List<String> list = new ArrayList<>();
for (String s : strings) {
if (!list.contains(s)) {
list.add(s);
}
}
return list.toArray(new String[0]);
}
}
Output:
[Hello, Hi, Bye]
As mentioned above, your best bet to avoid poor performance is to sort the array and look at adjacent entries for duplicates, acquiring the result array in two passes. Something along these lines:
public static String[] removeDuplicates(String[] strings) {
Arrays.sort(strings);
List<String> uniqueStrings = new ArrayList<>();
for (int i = 0; i < strings.length; i++) {
String currentString = strings[i];
uniqueStrings.add(currentString);
i++;
while (i < strings.length && currentString.equals(strings[i])) {
i++;
}
i--;
}
return uniqueStrings.toArray(new String[0]);
}
I'm assuming that you must use arrays and not Lists, Maps or any form of collections. So here is a simple approach.
Just check to see if the result array contains the current string. If it doesn't add it, else continue to the next string. Then at the end, copy the result to itself to eliminate the null values.
String[] a = {"A","E","B","A","C","D","C","E","A"};
int rcount = 0;
String[] result = new String[a.length];
outer:
for (String i : a) {
for (int k = 0; k < rcount; k++) {
if (i.equals(result[k])) {
continue outer;
}
}
result[rcount++] = i;
}
// now copy only the strings and reduce the size of the array.
result = Arrays.copyOf(result, rcount);
System.out.println(Arrays.toString(result))
Prints
[A, E, B, C, D]

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.

Using contains on an ArrayList with integer arrays

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

How can I find objects that are in BOTH arrays and promptly add it to another array?

How can I create a method that recieve two arrays as parameters and return an array filled with the items that were in both arrays?
Input (Array1 passed in method): ["Lisa", "George", "Mario"]
Input (Array2 passed in method): ["Luigi", "Susan", "Lisa"]
Method should return: ["Lisa"]
I cannot use any built in methods so I have to build my own algorithm, but I'm stuck for the past 2 hours. How can I achieve this in Java?
Edit: Christ on a candle stick. It's not for homework. I'm just real shitty at algorithms. Especially ones as basic as this, and especially on a foreign language I've never used. :P
How about:
private static String[] findCommon(final String[] array1,
final String[] array2) {
final Set<String> common = new HashSet<String>();
common.addAll(Arrays.asList(array1));
common.retainAll(Arrays.asList(array2));
return common.toArray(new String[0]);
}
One quick way is the following algorithm:
For each item in list1, add it to a dictionary.
For each item in list2, check if it exists in dictionary,
if item exists, add it to list3
else continue.
return list3.
Brute force.
Loop over the first array. Inside that loop, have another over the second array. Compare entries from both, adding them to a new array if you get a match.
[] result = ...
foreach( itemA in a ) {
foreach( iteamB in b ) {
if( itemB == itemA ) { // if they are in both places
r[] = itemB // append it to the result
}
}
}
return result;

Categories