Java Compare Two Lists - java

I have two lists ( not java lists, you can say two columns)
For example
**List 1** **Lists 2**
milan hafil
dingo iga
iga dingo
elpha binga
hafil mike
meat dingo
milan
elpha
meat
iga
neeta.peeta
I'd like a method that returns how many elements are same. For this example it should be
3 and it should return me similar values of both list and different values too.
Should I use hashmap if yes then what method to get my result?
Please help
P.S: It is not a school assignment :) So if you just guide me it will be enough

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

If you are looking for a handy way to test the equality of two collections, you can use org.apache.commons.collections.CollectionUtils.isEqualCollection, which compares two collections regardless of the ordering.

Are these really lists (ordered, with duplicates), or are they sets (unordered, no duplicates)?
Because if it's the latter, then you can use, say, a java.util.HashSet<E> and do this in expected linear time using the convenient retainAll.
List<String> list1 = Arrays.asList(
"milan", "milan", "iga", "dingo", "milan"
);
List<String> list2 = Arrays.asList(
"hafil", "milan", "dingo", "meat"
);
// intersection as set
Set<String> intersect = new HashSet<String>(list1);
intersect.retainAll(list2);
System.out.println(intersect.size()); // prints "2"
System.out.println(intersect); // prints "[milan, dingo]"
// intersection/union as list
List<String> intersectList = new ArrayList<String>();
intersectList.addAll(list1);
intersectList.addAll(list2);
intersectList.retainAll(intersect);
System.out.println(intersectList);
// prints "[milan, milan, dingo, milan, milan, dingo]"
// original lists are structurally unmodified
System.out.println(list1); // prints "[milan, milan, iga, dingo, milan]"
System.out.println(list2); // prints "[hafil, milan, dingo, meat]"

Of all the approaches, I find using org.apache.commons.collections.CollectionUtils#isEqualCollection is the best approach. Here are the reasons -
I don't have to declare any additional list/set myself
I am not mutating the input lists
It's very efficient. It checks the equality in O(N) complexity.
If it's not possible to have apache.commons.collections as a dependency, I would recommend to implement the algorithm it follows to check equality of the list because of it's efficiency.

Using java 8 removeIf
public int getSimilarItems(){
List<String> one = Arrays.asList("milan", "dingo", "elpha", "hafil", "meat", "iga", "neeta.peeta");
List<String> two = new ArrayList<>(Arrays.asList("hafil", "iga", "binga", "mike", "dingo")); //Cannot remove directly from array backed collection
int initial = two.size();
two.removeIf(one::contains);
return initial - two.size();
}

Simple solution :-
List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "d", "c"));
List<String> list2 = new ArrayList<String>(Arrays.asList("b", "f", "c"));
list.retainAll(list2);
list2.removeAll(list);
System.out.println("similiar " + list);
System.out.println("different " + list2);
Output :-
similiar [b, c]
different [f]

Assuming hash1 and hash2
List< String > sames = whatever
List< String > diffs = whatever
int count = 0;
for( String key : hash1.keySet() )
{
if( hash2.containsKey( key ) )
{
sames.add( key );
}
else
{
diffs.add( key );
}
}
//sames.size() contains the number of similar elements.

I found a very basic example of List comparison at List Compare
This example verifies the size first and then checks the availability of the particular element of one list in another.

public static boolean compareList(List ls1, List ls2){
return ls1.containsAll(ls2) && ls1.size() == ls2.size() ? true :false;
}
public static void main(String[] args) {
ArrayList<String> one = new ArrayList<String>();
one.add("one");
one.add("two");
one.add("six");
ArrayList<String> two = new ArrayList<String>();
two.add("one");
two.add("six");
two.add("two");
System.out.println("Output1 :: " + compareList(one, two));
two.add("ten");
System.out.println("Output2 :: " + compareList(one, two));
}

protected <T> boolean equals(List<T> list1, List<T> list2) {
if (list1 == list2) {
return true;
}
if (list1 == null || list2 == null || list1.size() != list2.size()) {
return false;
}
// to prevent wrong results on {a,a,a} and {a,b,c}
// iterate over list1 and then list2
return list1.stream()
.filter(val -> !list2.contains(val))
.collect(Collectors.toList())
.isEmpty() &&
list2.stream()
.filter(val -> !list1.contains(val))
.collect(Collectors.toList())
.isEmpty();
}

Related

How to remove all words from TreeSet ignore care

I created two sets:
public static Set<String> COMMON_ENGLISH_WORDS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
public static Set<String> NON_ENGLISH_WORDS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
I kept all my common english words arround 58000 and non english word arround 1700 in two files seperately. And I am loading from file and asigning to above two variables. That assigment is happening properly that I check by debug.
public static void finalNonEnglishWords(){
ToolsConstants.COMMON_ENGLISH_WORDS = CSVFileUtil.readCSVToTreeSet(ToolsConstants.COMMON_ENGLISH_WORDS_FILE);
ToolsConstants.NON_ENGLISH_WORDS = CSVFileUtil.readCSVToTreeSet(ToolsConstants.NON_ENGLISH_WORDS_FILE);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
ToolsConstants.NON_ENGLISH_WORDS.removeAll(ToolsConstants.COMMON_ENGLISH_WORDS);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
}
But it not removing.
I am seeing same number in output.
But I saw both files, there are some common words.
I did the same sample with just 7 elements but it working perfectly. And fallowed the same way only different is number of elements in collection.
public static void removeAllDemo(){
List<String> list1 = new ArrayList<>(
Arrays.asList("BOB", "Joe", "john", "MARK","MARk", "dave", "Bill")
);
List<String> list2 = Arrays.asList("JOE", "MARK", "DAVE", "Ravi");
// Add all values of list1 in a case insensitive collection
Set<String> set1 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set1.addAll(list1);
// Add all values of list2 in a case insensitive collection
Set<String> set2 = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
set2.addAll(list2);
// Remove all common Strings ignoring case
System.out.println(set1);
set1.removeAll(set2);
System.out.println(set1);
// Keep in list1 only the remaining Strings ignoring case
list1.retainAll(set1);
}
So generally you should apply the same approach as that implemented in the demo method:
appropriate empty sets with the case-insensitive order are already created
use addAll to populate the sets
remove duplicates from the set of non-English words
public static void finalNonEnglishWords() {
ToolsConstants.COMMON_ENGLISH_WORDS.addAll(
CSVFileUtil.readCSVToTreeSet(ToolsConstants.COMMON_ENGLISH_WORDS_FILE)
);
ToolsConstants.NON_ENGLISH_WORDS.addAll(
CSVFileUtil.readCSVToTreeSet(ToolsConstants.NON_ENGLISH_WORDS_FILE)
);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
ToolsConstants.NON_ENGLISH_WORDS.removeAll(
ToolsConstants.COMMON_ENGLISH_WORDS
);
System.out.println(ToolsConstants.NON_ENGLISH_WORDS.size());
}

Compare two lists and increase the counter on a match with java streams

I have two lists. list1 contains some citiys.
list2 contains sub-lists. Each sub-list contains the countries already visited by a person (one sub-list = the countries visited by one person). In the example Person1 has traveld to Rom, Amsterdam and Vienna, Person2 to Amsterdam, Barcelona and Milan ...
I would like to know how many people have already been to the countries in the first list. There should be no double counting. So if Person1 has already been to two countries from list1, it should only be counted once.
I would like to implement this with Java Streams. Does anyone know how I can do this?
list1 = ["Barcelona", "Milan", "Athens"];
list2 = [["Rom", "Amsterdam", "Vienna"], ["Amsterdam", "Barcelona", "Milan"], ["Prais", "Athens"], ["Istanbul", "Barcelona", "Milan", "Athens"]];
//The expected result for this example is: 3
//Both lists already result from a stream (Collectors.toList())
Thanks a lot!
You can try something like this:
private static final List<String> CITIES = List.of("Barcelona", "Milan", "Athens");
private static final List<List<String>> VISITED_CITIES = List.of(
List.of("Rome", "Amsterdam", "Vienna"),
List.of("Amsterdam", "Barcelona", "Milan"),
List.of("Paris", "Athens"),
List.of("Instabul", "Barcelon", "Milan", "Athens")
);
public static void main(String... args) {
var count = VISITED_CITIES
.stream()
.flatMap(visited -> visited.stream().filter(CITIES::contains))
.distinct()
.count();
System.out.println(count);
}
With this iteration you will get the expected result of 3. However you can modify your code to also collect into a Map that will show frequencies (if you remove the distinct intermediate step), something like this:
var count = VISITED_CITIES
.stream()
.flatMap(visited -> visited.stream().filter(CITIES::contains))
.collect(Collectors.groupingBy(Function.identity()));
Have a look at the mapToInt() and sum() function.
List<String> list1 = List.of("Barcelona", "Milan", "Athens");
List<List<String>> list2 = List.of(List.of("Rom", "Amsterdam", "Vienna"),
List.of("Amsterdam", "Barcelona", "Milan"),
List.of("Prais", "Athens"),
List.of("Istanbul", "Barcelona", "Milan", "Athens"));
int result = list2.stream().mapToInt(person -> person.stream().anyMatch(list1::contains) ? 1 : 0).sum();
What I do here is create a stream of all persons and then map each person to either 1 or 0 depending on if any of their visited countries is contained in list1.
This is identical with the following for-loop example:
int result = 0;
for (List<String> person : list2)
{
int i = 0;
for (String visited : person)
{
if (list1.contains(visited))
{
i = 1;
break;
}
}
result += i;
}

How to retrieve non-matched result from a list/array

I would like to compare the Objects of two Arrays and obtain a new Array with the Objects that did not match. This is:
Array1
Array2
both contain Object User with methods getId and getUsername
for (int fw = 0; fw < tempOldArray.size(); fw++) {
for (int fi = 0; fi < tempArray.size(); fi++) {
if (tempOldArray.get(fw).getId() == tempArray.get(fi).getId()) {
match++;
break;
}
if(fi == (tempArray.size()-1)) {
nomatchfound++;
break;
}
}
}
Array1: {[1231, Peter], [2562, Jackson], [38987, Robert], [4765, William]}
Array2: {[2562, Jackson], [7584, Alfred], [38987, Robert], [8123, Mozart]}
Array3 should output {[1231, Peter], [4765, William]}
and Array4 should output {[7584, Alfred], [8123, Mozart]}
Also questioned about how to retrieve a result from list
{"peter", "trump", "donald", "jerry"}
{"peter", "donald", "lucas", "jerry"}
and output non matching ones
You could use something like this. With this function it doesn't matter if you compare collection1 with collection2 or collection2 with collection1 if they have different sizes. The diff should be always equal.
private static <E> List<E> getDiff(Collection<E> collection1, Collection<E> collection2) {
Collection<E> largerOne = collection1.size() >= collection2.size() ? collection1 : collection2;
Collection<E> smallerOne = largerOne == collection1 ? collection2 : collection1;
return largerOne.stream().filter(i -> !smallerOne.contains(i)).collect(Collectors.toList());
}
If you are already using lists, just use something on the lines of
list1.removeAll(list2)
To further optimize, if you use a hashset, your remove operations become O(1) so it is even more efficient
This is simply a matter of discrete math. Check out the implementation of removeAll():
public static void main(String[] args) {
List<String> first = Arrays.asList("peter", "trump", "donald", "jerry");
List<String> second = Arrays.asList("peter", "donald", "lucas", "jerry");
List<String> results = new ArrayList<>(first);
results.removeAll(second);
System.out.println(results.toString());
}
Prints:
[trump]
This fulfills your requirement for leaving the first and second Lists intact, and creating a 3rd List to contain the result.
The other Answers showing List::removeAll such as the one from Cuga are correct and best for the simple case.
Java streams
I'll show the fancier approach using Java streams. You may want to use this approach if your situation gets more complicated.
Define your two lists. Using List.of to instantiate an unmodifiable list was added to Java 9.
List < String > namesA = List.of ( "Peter" , "Paul" , "Mary" , "Wendy" , "Lisa" );
List < String > namesB = List.of ( "Peter" , "Paul" , "Jesse" , "Wendy" , "Lisa" );
Create a stream from one list. For each element in the stream, see if that element can be found in the other list.
To find all the items that match, use.filter ( namesB :: contains ).
To find all the items that do not match (the distinct elements), use:.filter ( Predicate.not ( namesB :: contains ) )
That Predicate.not trick is new to Java 11, as shown here.
Collect the results into a new List.
List < String > distinct =
namesA.stream ()
.filter ( Predicate.not ( namesB :: contains ) )
.collect ( Collectors.toList () );
Dump to console.
System.out.println ( "namesA: " + namesA );
System.out.println ( "namesB: " + namesB );
System.out.println ( "distinct: " + distinct );
Results.
namesA: [Peter, Paul, Mary, Wendy, Lisa]
namesB: [Peter, Paul, Jesse, Wendy, Lisa]
distinct: [Mary]
Assuming the class you are using is called Person, you have to add the following equals method to the class definition
#Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (!Person.class.isAssignableFrom(obj.getClass())) {
return false;
}
final Person other = (Person) obj;
if (this.id != other.id) {
return false;
}
return true;
}
In this case you can simply use the removeAll method that's specified in the other answers like this.
list1.removeAll(list2);

How can I instantiate multiple objects into a list?

I need to instantiate a list with multiple String objects. I am not allowed to manually add objects 1 by 1.
I tried multiple ways, cannot get method to accept it.
scrambleOrRemove(["TAN", "ABRACADABRA", "WHOA", "APPLE", "EGGS"]);
Method:
public static void scrambleOrRemove(List<String> list)
{
int length = list.size()-1;
for(int i=0;i<length;i++)
{
String otherword = list.get(i);
if(scrambleWord(otherword).equals(otherword))
{
list.remove(i);
}
}
System.out.println(list);
}
Call it like this:
scrambleOrRemove(
new ArrayList<String>(
Arrays.asList(
"TAN", "ABRACADABRA", "WHOA", "APPLE", "EGGS"
)
)
);
Now that you have your solution, read the manuals and explain why this works and why it was necessary for your case.
You can use java.util.Arrays.asList() method. So for your example -
Arrays.asList({"TAN", "ABRACADABRA", "WHOA", "APPLE", "EGGS"});
Perhaps not relevant to your example as you are removing elements from your list later but you can also look into ImmutableList of Guava for doing this in general - https://google.github.io/guava/releases/snapshot/api/docs/com/google/common/collect/ImmutableList.html
ImmutableList.of("TAN", "ABRACADABRA", "WHOA", "APPLE", "EGGS");

Sorting arraylist in alphabetical order (case insensitive)

I have a string arraylist names which contains names of people. I want to sort the arraylist in alphabetical order.
ArrayList<String> names = new ArrayList<String>();
names.add("seetha");
names.add("sudhin");
names.add("Swetha");
names.add("Neethu");
names.add("ananya");
names.add("Athira");
names.add("bala");
names.add("Tony");
names.add("Karthika");
names.add("Nithin");
names.add("Vinod");
names.add("jeena");
Collections.sort(names);
for(int i=0; i<names.size(); i++)
System.out.println(names.get(i));
I tried to sort the list in above way. But it is displaying the sorted array as:
Athira
Karthika
..
..
ananya
bala
...
but I don't want to make it case sensitive. I want the result as:
ananya
Athira
bala
Custom Comparator should help
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return s1.compareToIgnoreCase(s2);
}
});
Or if you are using Java 8:
list.sort(String::compareToIgnoreCase);
The simplest thing to do is:
Collections.sort(list, String.CASE_INSENSITIVE_ORDER);
try this code
Collections.sort(yourarraylist, new SortBasedOnName());
import java.util.Comparator;
import com.RealHelp.objects.FBFriends_Obj;
import com.RealHelp.ui.importFBContacts;
public class SortBasedOnName implements Comparator
{
public int compare(Object o1, Object o2)
{
FBFriends_Obj dd1 = (FBFriends_Obj)o1;// where FBFriends_Obj is your object class
FBFriends_Obj dd2 = (FBFriends_Obj)o2;
return dd1.uname.compareToIgnoreCase(dd2.uname);//where uname is field name
}
}
Based on the above mentioned answers, I managed to compare my custom Class Objects like this:
ArrayList<Item> itemList = new ArrayList<>();
...
Collections.sort(itemList, new Comparator<Item>() {
#Override
public int compare(Item item, Item t1) {
String s1 = item.getTitle();
String s2 = t1.getTitle();
return s1.compareToIgnoreCase(s2);
}
});
Unfortunately, all answers so far do not take into account that "a" must not be considered equal to "A" when it comes to sorting.
String[] array = {"b", "A", "C", "B", "a"};
// Approach 1
Arrays.sort(array);
// array is [A, B, C, a, b]
// Approach 2
Arrays.sort(array, String.CASE_INSENSITIVE_ORDER);
// array is [A, a, b, B, C]
// Approach 3
Arrays.sort(array, java.text.Collator.getInstance());
// array is [a, A, b, B, C]
In approach 1 any lower case letters are considered greater than any upper case letters.
Approach 2 makes it worse, since CASE_INSENSITIVE_ORDER considers "a" and "A" equal (comparation result is 0). This makes sorting non-deterministic.
Approach 3 (using a java.text.Collator) is IMHO the only way of doing it correctly, since it considers "a"and "A" not equal, but puts them in the correct order according to the current (or any other desired) Locale.
You need to use custom comparator which will use compareToIgnoreCase, not compareTo.
Starting from Java 8 you can use Stream:
List<String> sorted = Arrays.asList(
names.stream().sorted(
(s1, s2) -> s1.compareToIgnoreCase(s2)
).toArray(String[]::new)
);
It gets a stream from that ArrayList, then it sorts it (ignoring the case). After that, the stream is converted to an array which is converted to an ArrayList.
If you print the result using:
System.out.println(sorted);
you get the following output:
[ananya, Athira, bala, jeena, Karthika, Neethu, Nithin, seetha, sudhin, Swetha, Tony, Vinod]
def lst = ["A2", "A1", "k22", "A6", "a3", "a5", "A4", "A7"];
println lst.sort { a, b -> a.compareToIgnoreCase b }
This should be able to sort with case insensitive but I am not sure how to tackle the alphanumeric strings lists
Kotlin. Sorting objects by string fields, case insensitive.
items?.sortedWith { o1, o2 ->
o1.name.compareTo(o2.name, true)
}
KOTLIN DEVELOPERS
For Custome List, if you want to sort based on one String then you can use this:
phoneContactArrayList.sortWith(Comparator { item, t1 ->
val s1: String = item.phoneContactUserName
val s2: String = t1.phoneContactUserName
s1.compareTo(s2, ignoreCase = true)
})

Categories

Resources