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.
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.
java - How to compare a field between two Lists of objects? - Stack Overflow
java - Comparing two lists by object fields - Stack Overflow
java - Determine if 2 lists with similar objects contain a partial duplicate - Code Review Stack Exchange
java - Comparing multiple fields of 2 list of different objects - Stack Overflow
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.
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;
}
I want to do a certain action each time an object is the same and at the same index and another action each time an object is present in both lists but not at the same index. It has to match though (i cant do the second action 4 times if both lists are like that ex :(r,f,f,f,f) (r, r, r, r, r) only once