You are right in saying that if they are arrays you can't use Arrays.equals(), if they are List<>s then you cannot just use equals. In both cases they also check order.
So yes your main option without writing your own comparison algorithm is to use Collections.sort() on both lists.
If you don't need to check for duplicates you could drop both lists into a HashSet using addAll and then compare the two HashSet or just use List.containsAll. The bad news though is that these would both have the same limitation, if you need to compare lists that might have repeated elements then it may give incorrect results. i.e. "bob", "bob", "fred" would compare as equal to "bob", "fred", "fred".
You are right in saying that if they are arrays you can't use Arrays.equals(), if they are List<>s then you cannot just use equals. In both cases they also check order.
So yes your main option without writing your own comparison algorithm is to use Collections.sort() on both lists.
If you don't need to check for duplicates you could drop both lists into a HashSet using addAll and then compare the two HashSet or just use List.containsAll. The bad news though is that these would both have the same limitation, if you need to compare lists that might have repeated elements then it may give incorrect results. i.e. "bob", "bob", "fred" would compare as equal to "bob", "fred", "fred".
Please refer to the example below:
public class Example{
public static void main(String[] args){
List<String> list1 = new ArrayList<String>();
List<String> list2 = new ArrayList<String>();
list1.add("A");
list1.add("B");
list1.add("C");
list2.add("C");
list2.add("X");
list2.add("Y");
for(String s : list1){
if(list2.contains(s)){
System.out.println("List 2 contains: " + s);
}
}
}
}
The code above is by all means not the cleanest or the most compact way to achieve what you are asking. But given the information presented, this should suffice.
Videos
Convert Lists to Collection and use removeAll
Collection<String> listOne = new ArrayList(Arrays.asList("a","b", "c", "d", "e", "f", "g"));
Collection<String> listTwo = new ArrayList(Arrays.asList("a","b", "d", "e", "f", "gg", "h"));
List<String> sourceList = new ArrayList<String>(listOne);
List<String> destinationList = new ArrayList<String>(listTwo);
sourceList.removeAll( listTwo );
destinationList.removeAll( listOne );
System.out.println( sourceList );
System.out.println( destinationList );
Output:
[c, g]
[gg, h]
[EDIT]
other way (more clear)
Collection<String> list = new ArrayList(Arrays.asList("a","b", "c", "d", "e", "f", "g"));
List<String> sourceList = new ArrayList<String>(list);
List<String> destinationList = new ArrayList<String>(list);
list.add("boo");
list.remove("b");
sourceList.removeAll( list );
list.removeAll( destinationList );
System.out.println( sourceList );
System.out.println( list );
Output:
[b]
[boo]
This should check if two lists are equal, it does some basic checks first (i.e. nulls and lengths), then sorts and uses the collections.equals method to check if they are equal.
public boolean equalLists(List<String> a, List<String> b){
// Check for sizes and nulls
if (a == null && b == null) return true;
if ((a == null && b!= null) || (a != null && b== null) || (a.size() != b.size()))
{
return false;
}
// Sort and compare the two lists
Collections.sort(a);
Collections.sort(b);
return a.equals(b);
}
Don't use != to compare strings. Use the equals method :
if (! Blist.get(i).equals(Alist.get(j))
But this wouldn't probably fix your algorithmic problem (which isn't clear at all).
If what you want is know what items are the same at the same position, you could use a simple loop :
int sizeOfTheShortestList = Math.min(Alist.size(), Blist.size());
for (int i=0; i<sizeOfTheShortestList; i++) {
if (Blist.get(i).equals(Alist.get(i))) {
System.out.println("Equals..: " + Blist.get(i));
}
}
If you want to get items that are in both lists, use
for (int i = 0; i < Alist.size(); i++) {
if (Blist.contains(Alist.get(i))) {
System.out.println("Equals..: " + Alist.get(i));
}
}
You can use the RemoveAll(Collection c) on one of the lists, if you happen to know if one list always contains them all.
EDIT
Here are two versions. One using ArrayList and other using HashSet
Compare them and create your own version from this, until you get what you need.
This should be enough to cover the:
P.S: It is not a school assignment :) So if you just guide me it will be enough
part of your question.
continuing with the original answer:
You may use a java.util.Collection and/or java.util.ArrayList for that.
The retainAll method does the following:
Retains only the elements in this collection that are contained in the specified collection
see this sample:
import java.util.Collection;
import java.util.ArrayList;
import java.util.Arrays;
public class Repeated {
public static void main( String [] args ) {
Collection listOne = new ArrayList(Arrays.asList("milan","dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta"));
Collection listTwo = new ArrayList(Arrays.asList("hafil", "iga", "binga", "mike", "dingo"));
listOne.retainAll( listTwo );
System.out.println( listOne );
}
}
EDIT
For the second part ( similar values ) you may use the removeAll method:
Removes all of this collection's elements that are also contained in the specified collection.
This second version gives you also the similar values and handles repeated ( by discarding them).
This time the Collection could be a Set instead of a List ( the difference is, the Set doesn't allow repeated values )
import java.util.Collection;
import java.util.HashSet;
import java.util.Arrays;
class Repeated {
public static void main( String [] args ) {
Collection<String> listOne = Arrays.asList("milan","iga",
"dingo","iga",
"elpha","iga",
"hafil","iga",
"meat","iga",
"neeta.peeta","iga");
Collection<String> listTwo = Arrays.asList("hafil",
"iga",
"binga",
"mike",
"dingo","dingo","dingo");
Collection<String> similar = new HashSet<String>( listOne );
Collection<String> different = new HashSet<String>();
different.addAll( listOne );
different.addAll( listTwo );
similar.retainAll( listTwo );
different.removeAll( similar );
System.out.printf("One:%s%nTwo:%s%nSimilar:%s%nDifferent:%s%n", listOne, listTwo, similar, different);
}
}
Output:
$ java Repeated
One:[milan, iga, dingo, iga, elpha, iga, hafil, iga, meat, iga, neeta.peeta, iga]
Two:[hafil, iga, binga, mike, dingo, dingo, dingo]
Similar:[dingo, iga, hafil]
Different:[mike, binga, milan, meat, elpha, neeta.peeta]
If it doesn't do exactly what you need, it gives you a good start so you can handle from here.
Question for the reader: How would you include all the repeated values?
You can try intersection() and subtract() methods from CollectionUtils.
intersection() method gives you a collection containing common elements and the subtract() method gives you all the uncommon ones.
They should also take care of similar elements
one way is, put the elements in the two lists into a Set, check if the set.size() is same as list1.size()+list2.size()
The diff would be the count of duplicated elements.
try this :
ArrayList intersection = new ArrayList<>(list1);
intersection.retainAll(list2);
intersection will contain the common elements
First of all, the existing solution with a loop is just fine. Simply translating it to a solution that uses streams is not an improvement.
Here's how I would do it. NB: this is not tested.
// Naive version
List<String> nameList1 = Arrays.asList("Bill", "Steve", "Mark");
List<String> nameList2 = Arrays.asList("Steve Jobs", "Mark", "Bill");
List<String> matchList = nameList1.stream()
.filter(nameList2::contains)
.limit(1)
.collect(Collectors.toList());
count = matchList.size();
Note that there is no need to both append to a list and increment a count.
If you were going to try to parallelize this, then streams could help, but you would need to do it differently.
Finally, if you are really concerned about performance, and nameList2.size() == N is large enough, it will be faster to convert nameList2 to a HashSet. This turns this from an O(MN) algorithm to an O(M) algorithm.
(By contrast, parallelizing the naive version only gives you O(MN/P) complexity at best where P is the number of processes. And that is making some assumptions, and ignoring potential memory contention effects.)
tl;dr
If you just want a count of one or zero, for a first match found or no match found, make a stream from the first list, filter with a predicate testing if the second list contains each streamed item from the first list, limit to the first match found, and returning via a ternary statement either number 1 or 0.
int count =
List.of( "Bill" , "Steve" , "Mark" )
.stream()
.filter( ( String s ) -> List.of( "Steve Jobs" , "Mark" , "Bill" ).contains( s ) )
.findFirst()
.isPresent()
? 1 : 0
;
Not that I recommend that one-liner. See below for more practical code. And see the correct Answer by Stephen C for a variation on this theme.
Details
Given two lists of String objects:
List < String > these = List.of( "Bill" , "Steve" , "Mark" );
List < String > those = List.of( "Steve Jobs" , "Mark" , "Bill" );
List of matches
You can get a List of the items that match.
List < String > matches = these.stream().filter( s -> those.contains( s ) ).collect( Collectors.toList() );
Single match
But you apparently are asking for only the first item that happens to match.
If that is your goal, you use of a List for the result, as well as the counter make no sense. You can only ever have one or zero objects as a result.
Optional
None of the items may match, so we might not get an item back. For this purpose the Optional class was invented, to cover the case of returning either a single object or no object (null). The Optional class protects against a NullPointerException from bungled handling of a return value that is NULL.
Optional < String > match = // `Optional` is a class wrapping another object, the payload, which may be NULL.
these
.stream() // Produce a stream of items from this `List`.
.filter( // Apply a predicate test against each item produced in the stream. Items that pass the test are fed into a new second stream.
( String s ) -> those.contains( s ) // Predicate.
) // Returns another `Stream`.
.findFirst() // Halts the new (second) stream after producing the first item. Returns an `Optional` object, a wrapper around the payload which may be NULL.
;
You can ask the Optional object if it contains a String object or if it is empty. If not you can ask for an alternate value to use as a default. You could throw an NoSuchElementException if you expect there always to be a match. Or you can perform your own logic in case of an empty Optional. See the class doc and search Stack Overflow to learn more about Optional.
if( match.isPresent() ) {
System.out.println( "Match found: " + match.get() ) ;
}
Dump to console.
System.out.println( "these = " + these );
System.out.println( "those = " + those );
System.out.println( "matches = " + matches );
System.out.println( "match = " + match );
these = [Bill, Steve, Mark]
those = [Steve Jobs, Mark, Bill]
matches = [Bill, Mark]
match = Optional[Bill]
You don't need to compare
List<Integer> c = new ArrayList<>(a);
c.removeAll(b);
And if you don't mind loosing the original list data
a.removeAll(b);
Something like this should suffice:
Set<Integer> container = new HashSet<>(ListB);
ListA.stream()
.filter(id -> !container.contains(id))
.forEach(System.out::println);
or non-stream:
Set<Integer> container = new HashSet<>(ListB);
for(Integer id : ListA)
if(!container.contains(id));
System.out.println(id);
Try fnlData.equals(fnlDataTMP) if both list are in order
or if order does not matter, try creating hash set and then compare using equals
new HashSet(fnlData).equals(new HashSet(fnlDataTMP))
I don't think there is a way that let's you achieve that out of the box.
You can do something like the functional java List.join method to quickly generate 2 Lists and compare these:
List<String> joinedFnlData = jf.data.List.join(fnlData);
List<String> joinedFnlDataTMP = jf.data.List.join(fnlDataTMP);
CollectionUtils.isEqualCollection(joinedFnlData, joinedFnlDataTMP);
Things to note:
- This is probably not the cheapest operation - so it should not be invoked too often in a time critical scenario (e.g. UI thread)
- It does not do a "real" equals - for that you would have to do a nested loop like in the above answer. This checks that both joined lists have the same elements with the same cardinality: e.g. if fnlData has 2 lists with "1" and "2" as the only elements and fnlDataTMP has 1 list with "1", "2" as the elements, this would mark both as equal. Depending on your scenario this might be irrelevant - if this is relevant I don't see a way around nested loops.
You could use isEqualList from Apache's ListUtils:
isEqualList
public static boolean isEqualList(java.util.Collection list1,
java.util.Collection list2)
Tests two lists for value-equality as per the equality contract in List.equals(java.lang.Object).
This method is useful for implementing List when you cannot extend AbstractList. The method takes Collection instances to enable other collection types to use the List implementation algorithm.
The relevant text (slightly paraphrased as this is a static method) is:
Compares the two list objects for equality. Returns true if and only if 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. Note: The behaviour of this method is undefined if the lists are modified during the equals comparison.
It is the correct way. Your lists are not equal as you say they are. Try printing them for visual inspection.