You can make your Student override the .equals(Object o) method.
public class Student {
private int id;
private int classId;
private String name;
private int age;
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (classId != other.classId)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
Then you can compare two students like so:
Student s1 = ...;
Student s2 = ...;
if(s1.equals(s2)) {
//Do this when they are equal
}
This is a clean way to check for equality and all of Java's functions will call this overrided equals method. You can also use it with lists.
List<Student> studentList = ...;
Student s1 = ...;
if(studentList.contains(s1)) {
//Do this when s1 is in the list
}
Please let me know if you have any questions or if I misunderstood your question.
Answer from RareTyler on Stack Overflowjava - How to compare a field between two Lists of objects? - Stack Overflow
How to compare two different list object in java? - Stack Overflow
Java Compare Two List's object values? - Stack Overflow
java - Determine if 2 lists with similar objects contain a partial duplicate - Code Review Stack Exchange
I'd make a string id+title for each object, in 2 lists. Then compare the 2 lists
List<String> aList = aObjectList.stream()
.map(a -> a.getaId() + a.getaTitle())
.collect(Collectors.toList());
List<String> bList = bObjectList.stream()
.map(b -> b.getbId() + b.getbTitle())
.collect(Collectors.toList());
boolean sameElements = aList.size() == bList.size() &&
aList.containsAll(bList) &&
bList.containsAll(aList);
It could make sense to merge id / title into a single String, remap the input lists into List<String> and then use AssertJ hasSameElements to compare the new lists:
assertThat(
aObjectList.stream()
.map(a -> String.join(":", a.aId, a.aTitle))
.collect(Collectors.toList())
).hasSameElementsAs(
bObjectList.stream()
.map(b -> String.join(":", b.bId, b.bTitle))
.collect(Collectors.toList())
);
One solution :
- Construct a Map of the Local supermarkets using the supermarketId as the key by running through the list once
- Loop through the cloud list and do you comparison, looking up the local supermarket from your map.
i.e. O(n) instead of O(n2)
Here's a two-line solution:
Map<String, Supermarket> map = getFromLocal().stream()
.collect(Collectors.toMap(s -> s.supermarketId, s -> s));
List<Supermarket> hasDiffLastItem = getFromCloud().stream()
.filter(s -> !map.get(s.supermarketId).lastItemBoughtId.equals(s.lastItemBoughtId))
.collect(Collectors.toList());
I would put one of the lists in a Map with as key the Supermarket ID and as value the supermarket instance then iterate over the other getting from the Map and comparing the lastItemBoughtId.
I'm not sure I fully understand the question, but you're asking for something like this?
public boolean compare (List<AClass> listA, List<BClass> listB) {
if (listA.size() != listB.size ()) {
return false;
}
for (int i=0; i<listA.size(); i++) {
AClass aClass = (AClass) listA.get(i);
BClass bClass = (BClass) listB.get(i);
// This is example for numeric comparison
// For String, use !equals()
if (aClass.commonField != bClass.commonField) return false;
}
return true;
}
Both lists should be sorted by that commonField field
Checkout the javadoc for List.equals():
two lists are defined to be equal if they contain the same elements in the same order.
The equals method can be used here and of course ignores the generic types you have declared the two lists to be. The equals method for AbstractList (which ArrayList and a bunch of other list classes implements) will iterate over both lists and call equals on each of the elements in order, once done it makes sure there aren't any items left in either list.
Given all this, you will be able to call the equals method to determine weather two lists contain the same elements:
List<Aclass> A1 = new ArrayList<Aclass>();
List<Bclass> b1 = new ArrayList<Bclass>();
// getElement returns a private field that can be cast to both Aclass and Bclass
A1.add(getElement());
b1.add(getElement());
if (A1.equals(b1)) {
System.out.println("two lists are equal");
}
It's not the most efficient solution but the most terse code would be:
boolean equalLists = listA.size() == listB.size() && listA.containsAll(listB);
Update:
@WesleyPorter is right. The solution above will not work if duplicate objects are in the collection.
For a complete solution you need to iterate over a collection so duplicate objects are handled correctly.
private static boolean cmp( List<?> l1, List<?> l2 ) {
// make a copy of the list so the original list is not changed, and remove() is supported
ArrayList<?> cp = new ArrayList<>( l1 );
for ( Object o : l2 ) {
if ( !cp.remove( o ) ) {
return false;
}
}
return cp.isEmpty();
}
Update 28-Oct-2014:
@RoeeGavriel is right. The return statement needs to be conditional. The code above is updated.
ArrayList already have support for this, with the equals method. Quoting the docs
... In other words, two lists are defined to be equal if they contain the same elements in the same order.
It does require you to properly implement equals in your MyData class.
Edit
You have updated the question stating that the lists could have different orders. In that case, sort your list first, and then apply equals.
The approach, algorithm
To find if an object exists in a list, you need to perform a linear search, potentially visiting every single element, in \$O(n)\$ time. More efficient data structures exist:
Use an ordered data structure: if the values are sorted, then you can find if an element exists using binary search, in \$O(\log n)\$ time.
Use a hashset: you can find if an element is in the set in constant time, \$O(1)\$
To be to search efficiently, use a hashset instead of a list.
However, to be able to use a hashset efficiently,
it is required that the objects you put in it have appropriate implementation of hashCode and equals methods.
See the official tutorial on the Object class,
especially the sections on the equals and hashCode methods.
Note that IDEs like IntelliJ and Eclipse can generate these methods for you easily (they are boring to write by hand, and usually there's little reason to do so).
With correct implementation of the equals and hashCode methods,
for example as in the other answer by @Teddy,
your main program could be reduced to this:
public static void main(String[] args) {
Set<Person> originalPeople = new HashSet<>();
Set<Person> newPeople = new HashSet<>();
originalPeople.add(new Person("William", "Tyndale"));
originalPeople.add(new Person("Jonathan", "Edwards"));
originalPeople.add(new Person("Martin", "Luther"));
newPeople.add(new Person("Jonathan", "Edwards"));
newPeople.add(new Person("James", "Tyndale"));
newPeople.add(new Person("Roger", "Moore"));
for (Person original : originalPeople) {
if (!newPeople.contains(original)) {
System.out.printf("%s %s is not in the new list!%n",
original.getFirstName(), original.getLastName());
}
}
}
There are certain reasons I would not prefer your approach. These are:
1) There are multiple method calls which reduces the readability.
2) You are using filter twice which decreases the performances. You could do it inside the same filter like I've shown below
3) With this approach you don't have a consolidated List which contains common elements. For that you need another else condition which increases the cyclomatic complexity.
4) A null check which could produce NPE if not handled carefully as you are using findFirst() which returns Optional (Although null can be replaced with default new Person("","") object which is again not recommended).
5) Finally don't use ArrayList<Person> originalPeople = new ArrayList<>();; rather declare as List, to follow programming to interface.
Rather I would use below approach:
private static List<Person> getPersonInList(
final List<Person> newPeople, List<Person> originalPeople) {
List<Person> list = new ArrayList<>();
newPeople.forEach(p ->
originalPeople.stream()
.filter(p1 -> p.getFirstName().equals(p1.getFirstName()) &&
p.getLastName().equals(p1.getLastName()))
.forEach(list::add));
return list;
}
Override the equals and hashcode method based on first five keys. Then you can just use equals method to compare the objects and use Collections bulk operations like retainAll etc.
You could overwrite Equals and HashCode of CustomObj. Then use Contains() to test uniqueness.
With Java 8:
Comparator.comparing((Person p)->p.firstName)
.thenComparing(p->p.lastName)
.thenComparingInt(p->p.age);
If you have accessor methods:
Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge);
If a class implements Comparable then such comparator may be used in compareTo method:
@Override
public int compareTo(Person o){
return Comparator.comparing(Person::getFirstName)
.thenComparing(Person::getLastName)
.thenComparingInt(Person::getAge)
.compare(this, o);
}
You should implement Comparable <Person>. Assuming all fields will not be null (for simplicity sake), that age is an int, and compare ranking is first, last, age, the compareTo method is quite simple:
public int compareTo(Person other) {
int i = firstName.compareTo(other.firstName);
if (i != 0) return i;
i = lastName.compareTo(other.lastName);
if (i != 0) return i;
return Integer.compare(age, other.age);
}