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

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);

Related

Sort ArrayList of Objects, WITHOUT Class Getters. Possible?

I'm trying to use a lambda expression to sort an ArrayList of Objects (i.e. rows imported from a CSV file), WITHOUT classes (like "Employee", or "Person", or "Animal", etc.), so there are no Getters/Setters for the Objects at all. So I'm essentially BufferedReading in rows from a CSV file, assigning them to objects, and adding those objects to an ArrayList. I want to then sort by the last value/index in each object. For example...
These are my CSV rows:
Mark,10,0,34
Tom,2,0,19
Billy,2,0,7
...and I would like to sort them by the far right numbers in ascending order, so my sorted ArrayList would look like:
Billy,2,0,7
Tom,2,0,19
Mark,10,0,34
Is this even possible using a lambda expression? What I have for code so far looks like this:
// Import the csv file and assign each row to a list of objects
List<Object> olReturn = null;
try (BufferedReader br = new BufferedReader(new FileReader("stats_log.csv"))) {
olReturn = br.lines().sorted((x,y) -> x.LASTVALUEHERE() - y.LASTVALUEHERE()).collect(Collectors.toList());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
for (Object obIndObject : olReturn) {
System.out.println(obIndObject);
}
I know how to do it if the object classes had getters for the values I want to sort by, but that doesn't apply here, so just seeing if there's a potential solution, or if it's impossible?
tl;dr
List < String > sorted =
List.of(
"Mark,10,0,34" ,
"Tom,2,7,19" ,
"Billy,2,0,7"
)
.stream()
.sorted( ( String s1 , String s2 ) -> Integer.valueOf( s1.split( "," )[ 3 ] ).compareTo( Integer.valueOf( s2.split( "," )[ 3 ] ) ) )
.toList();
Streams
You asked:
use a lambda expression to sort an ArrayList of Objects (i.e. rows imported from a CSV file),
In other words, you want to sort a list of String objects by the last part delimited with a COMMA (,).
For each string, we need to do a String#split call, returning an array of parts. We expect exactly four parts given your example data. So take the last part. Arrays in Java annoyingly use an index, that is, zero-based counting. So the 4th item is an index of 3, syntactically [3].
This code is not efficient, as sorting involves many repetitive calls to compareTo. For each call we are performing the splitting all over again. Not efficient, but it works.
List < String > inputs =
new ArrayList <>(
List.of(
"Mark,10,0,34" ,
"Tom,2,7,19" ,
"Billy,2,0,7"
)
);
List < String > sorted =
inputs
.stream()
.sorted( ( String s1 , String s2 ) -> Integer.valueOf( s1.split( "," )[ 3 ] ).compareTo( Integer.valueOf( s2.split( "," )[ 3 ] ) ) )
.toList(); // Before Java 16: .collect( Collectors.toList() );
System.out.println( "sorted = " + sorted );
See this code run live at IdeOne.com.
sorted = [Billy,2,0,7, Tom,2,7,19, Mark,10,0,34]
Records
Your Question really does not make sense in that it explicitly avoids using classes. Java is an object-oriented language. Classes are its bread-and-butter, its raison d'ĂȘtre.
If you are simply reluctant to define a class because of the requisite boilerplate, I have good news for you. Java 16 brings the new records feature.
Records are a brief way to declare a class whose main purpose is to communicate data transparently and immutably. You simply declare the types and names of all the class member fields. The compiler implicitly creates the constructor, getters, equals & hashCode, and toString.
So an entire class can be written on one short line.
record Person ( String name , int departmentId , int projectId , int employeeId ) {}
Populate a collection of Person objects based on your input data.
List < Person > persons = new ArrayList <>( inputs.size() );
for ( String input : inputs )
{
String[] parts = input.split( "," );
Person p = new Person( parts[ 0 ] , Integer.valueOf( parts[ 1 ] ) , Integer.valueOf( parts[ 2 ] ) , Integer.valueOf( parts[ 3 ] ) );
persons.add( p );
}
Sort that list by the last member field, employeeId.
Collections.sort(
persons ,
new Comparator < Person >()
{
#Override
public int compare ( Person p1 , Person p2 )
{
return Integer.compare( p1.employeeId() , p2.employeeId() );
}
}
);
Here is complete example.
package work.basil.demo;
import java.util.*;
import java.lang.*;
public class RecordDemo
{
public static void main ( String[] args )
{
List < String > inputs =
new ArrayList <>(
List.of(
"Mark,10,0,34" ,
"Tom,2,7,19" ,
"Billy,2,0,7"
)
);
record Person(String name , int departmentId , int projectId , int employeeId)
{
}
List < Person > persons = new ArrayList <>( inputs.size() );
for ( String input : inputs )
{
String[] parts = input.split( "," );
Person p = new Person( parts[ 0 ] , Integer.valueOf( parts[ 1 ] ) , Integer.valueOf( parts[ 2 ] ) , Integer.valueOf( parts[ 3 ] ) );
persons.add( p );
}
System.out.println( "persons = " + persons );
Collections.sort(
persons ,
new Comparator < Person >()
{
#Override
public int compare ( Person p1 , Person p2 )
{
return Integer.compare( p1.employeeId() , p2.employeeId() );
}
}
);
System.out.println( "persons = " + persons );
}
}
When run.
persons = [Person[name=Mark, departmentId=10, projectId=0, employeeId=34], Person[name=Tom, departmentId=2, projectId=7, employeeId=19], Person[name=Billy, departmentId=2, projectId=0, employeeId=7]]
persons = [Person[name=Billy, departmentId=2, projectId=0, employeeId=7], Person[name=Tom, departmentId=2, projectId=7, employeeId=19], Person[name=Mark, departmentId=10, projectId=0, employeeId=34]]
In that code above, notice another nice thing about records: You can declare records locally, within a method, as well as nested in a class or in a separate class (.java file). And while the Java team was at it, they now allow local enum definitions, and local interface definitions too. All that is in Java 16 and later.
I updated based on the OP's comment
public static void main (String[] args) {
List<String> list =
Arrays.asList("Mark,10,0,34", "Wyle,2,0,19", "Tom,2,0,19", "Billy,2,0,7");
Comparator<String> cmp = new Comparator<String>() {
public int compare (String o1, String o2) {
int diff = (Integer.valueOf(o1.substring(o1.lastIndexOf(',')+1))).compareTo(Integer.valueOf(o2.substring(o2.lastIndexOf(',')+1)));
return (diff == 0)
? o1.compareTo(o2)
: diff;
}
};
Collections.sort(list, cmp);
System.out.println(list);
}
This outputs:
[Billy,2,0,7, Tom,2,0,19, Wyle,2,0,19, Mark,10,0,34]
This example will compare based on the last set of numbers OR alphabetically cases where the last set of numbers are the same.
Ok, it's not using a lambda expression, however I was able to modify the solution found here to fit my needs. Take a look:
This is the custom Comparator class:
private class MyObjectComparator<MyObject> implements Comparator<MyObject> {
/**
* {#inheritDoc}
*/
#Override
public int compare(MyObject o1, MyObject o2) {
return Integer.parseInt(o1.toString().split(",")[3]) - Integer.parseInt(o2.toString().split(",")[3]);
}
}
As you can see, I parse the integer of the 3rd indexed value after splitting the string. In other words, I take each string "object", split them, then reference the value I want to reference. Then, I just call this class using:
// Import the csv file and assign each row to a list of objects
List<Object> olReturn = null;
try (BufferedReader br = new BufferedReader(new FileReader("stats_log.csv"))) {
olReturn = br.lines().collect(Collectors.toList());
// call to comparator here
Collections.sort(olReturn, new MyObjectComparator());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
and this gets me what I need. Thanks for working that out with me!
br.lines() return Stream of String, so underlying object would be String, therefore you just need to do some String indexing to get what you need
olReturn = br.lines().sorted(Comparator.comparing(x -> Long.valueOf(x.substring(x.lastIndexOf(",") + 1)))).collect(Collectors.toList());
You should avoid calculating split and Integer.parseInt in Comparator as it is inefficient.
Try this.
List<String> list =
Arrays.asList("Mark,10,0,34", "Wyle,2,0,19", "Tom,2,0,19", "Billy,2,0,7");
List<String> result = list.stream()
.map(line -> new Object() {
int sortKey = Integer.parseInt(line.split(",")[3]);
String self = line;
})
.sorted(Comparator.comparingInt(object -> object.sortKey))
.map(object -> object.self)
.collect(Collectors.toList());
System.out.println(result);
output:
[Billy,2,0,7, Wyle,2,0,19, Tom,2,0,19, Mark,10,0,34]

Filtering objects with circular reference to itself (ObjectA has property List<ObjectA>)

I have a large json structure with nested values that I have converted into a list of objects to work with. I'd like to filter out all objects that don't contain a specific property value. Problem is though, that so far all I've come up with is a for loop within a for loop within a for loop (and that's given we know the json structure is only three nested levels). I only want to filter out the objects that do contain an integer (if it's null, it could be a parent containing something valid) or parents that are empty). If I try to stream with flattened - I can filter out all my objects and nested objects but won't I lose my structure?
quick eg.
public class ObjectA {
Integer id;
List<ObjectA> sublist;
}
List<ObjectA> fullList;
Set<Integer> keeptheseIntegers;
for (ObjectA obj : fullList) {
if (obj.getId() != null && !keeptheseIntegers.contains(obj.getId()){
fullList.remove(obj);
} else if (obj.getId() == null && obj.getSubList().size() > 0) {
for (ObjectA subObj : obj.getSubList()){
(same thing as above)
}
}
edit - I did realize later that the remove was not working properly and used iterator.remove. still same logical issue though
First: instead of manipulating your original structure (remove unwanted) I would collect the wanted items into an own list during the algorithm.
Second: Iterating through nested structures is a good candidate for the recursive pattern.
Third: Using java 8 I would implement it using streams and lambdas.
Something like this:
public class ObjectA
{
Integer id;
List<ObjectA> sublist;
}
private static final Set<Integer> keeptheseIntegers = new HashSet<>();
static
{
keeptheseIntegers.add( 1 );
}
private List<ObjectA> filter( List<ObjectA> list)
{
List<List<ObjectA>> subLists = new ArrayList<>();
List<ObjectA> result = list.stream()
// get all objects with sublist and add the sublist to interim subLists:
.peek( obj -> {
if ( obj.sublist == null )
{
// assert your assumption that Integer is assigned
if ( obj.id == null )
{
throw new IllegalArgumentException();
}
}
else
{
subLists.add( obj.sublist );
}
} )
// filter for the objects you want in result:
.filter( (obj -> obj.id != null && keeptheseIntegers.contains(obj.id)))
// and convert the resulting stream to a list:
.collect( Collectors.toList());
// call this method recusively for all found sublists:
subLists.forEach( i -> result.addAll(filter( i)) );
return result;
}
and somewher in your main program flow you call it:
...
List<ObjectA> fullList = new ArrayList<>();
List<ObjectA> objWithInt = filter( fullList);
// process the received list. Your original fullList is unchanged.

Remove all repeated list members

I have an Array full of Objects, and if two Objects are the same, I want to delete both of them.
Here is my current approach, which returns a
java.util.ConcurrentModificationException
public void deleteDuplicates(ArrayList<Object> objectArrayList){
Iterator<Object> objectIterator = objectArrayList.iterator();
Iterator<Object> objectIterator2 = objectArrayList.iterator();
while(objectIterator.hasNext()){
Object object = objectIterator.next();
while(objectIterator2.hasNext()){
if(object.equals(objectIterator2.next())){
objectIterator2.remove();
objectIterator.remove();
}
}
}
}
This code will remove all list members that are present more than once.
This approach handles an odd number of repeats. So it does not detect and remove repeat pairs, because that could leave an orphan repeat. Instead, it counts first, and then removes repeats.
public static void deleteDuplicates( List<Object> objectList) {
HashMap<Object,Integer> counts = new HashMap<>();
for ( Object o : objectList ) {
int oldCount = counts.containsKey( o )
? counts.get( o ).intValue()
: 0;
counts.put( o, oldCount + 1 );
}
for ( Iterator<Object> it = objectList.iterator(); it.hasNext(); ) {
Object o = it.next();
if ( 1 < counts.get( o )) {
it.remove();
}
}
}
Note that this approach requires that the hashCode() method of the list members satisfy the contract specified by Object.hashCode(), including:
If two objects are equal according to the equals(Object) method, then calling the hashCode method on each of the two objects must produce the same integer result.

Convert List of object arrays to list of objects in Java

Is there any Utility function in Java to convert List<Object[]> to List<Object>
No, there's no method that does this directly for you. You can write a nested for loop or use the flatMap of the stream API as follows:
List<Object> flat = objArrs.stream()
.flatMap(Stream::of)
.collect(Collectors.toList());
You can easily write one yourself:
public static<T> List<T> append (List<T[]> input) {
List<T> res = new ArrayList<T>();
for(T[] subarr : input) {
if(subarr != null) {
int n = subarr.length;
for(int i = 0; i < n; i++) {
res.add(subarr[i]);
}
}
}
return res;
}
The function appends the different arrays and null arrays are ignored, null elements are however not. Thus if the input is [null,[a,b],[null,null],[c,null,d],null]. The output is [a,b,null,null,c,null,d].
No.
Why don't you just write the function yourself? It would probably be faster than asking this question and waiting for an answer.
In Java 8 you can do it with Streams :
List<Object[]> list = ...
List<Object> l = list.stream()
.flatMap(arr -> Stream.of(arr))
.collect(Collectors.toList());
As others have already said, there is no utility and creating one yourself wouldn't be hard, for example using old school for loops:
public List<Object> flatten( List<Object[]> source )
{
// if ( source == null ) return null; // which check you use it up to you
List<Object> result = new ArrayList<Object>();
if ( source == null ) return result; // Personally I like this check
for ( Object[] array: source )
{
if ( array == null ) continue; // skip nulls
for ( Object object: array )
{
result.add(object);
}
}
return result;
}

Java Compare Two Lists

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();
}

Categories

Resources