Compare two lists of different objects by certain two fields
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())
);
Compare two objects excluding some fields - Java
The quickest way without writing any code is Lombok
Lombok is one of the most used libraries in java and it takes a lot of Boilerplate code off your projects. If you need to read more on what it can and does, go here.
The way to implement what you need is pretty straightforward:
// Generate the equals and HashCode functions and Include only the fields that I annotate with Include
@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public class Class1
{
@EqualsAndHashCode.Include // Include this field
private Long identity;
private String testStr1; // This field is not annotated with Include so it will not be included in the functions.
// ... any other fields
}
Lombok can do a lot more than this. For more information on @EqualsAndHashCode
refer to this.
You can always use @EqualsAndHashCode.Exclude
for a quicker solution to your use case:
@EqualsAndHashCode
@Getter // Generate getters for each field
@Setter // Generate setters for each field
public final class Class1 {
private String a;
private String b;
private String c;
:
:
:
private String z;
@EqualsAndHashCode.Exclude
private Date createdAt;
@EqualsAndHashCode.Exclude
private Date updatedAt;
}
How to compare a field between two Lists of objects?
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());
Comparing two objects for varying set of properties
anyMatch
You can solve this with the Stream API, using anyMatch
, with your rules basically being defined as Predicate
s.
For example checking if there is any person who is 20
years old:
List<Person> persons = ...
boolean doesAnyMatch = persons.stream()
.anyMatch(p -> p.getAge() == 20);
You can of course also setup the rule in a way that it compares with an existing item, mimicking equals
a bit more:
p -> p.getAge() == otherPerson.getAge()
Predicate
You can setup all your rules somewhere else, as Predicate
s and then use them. For example:
List<Predicate<Person>> rules = List.of(
p -> p.getAge() == 20,
p -> p.getName().equals("John"),
p -> p.getAge() > 18,
p -> p.getName().length() > 10 && p.getAge() < 50
);
And then maybe use them in some sort of loop, whatever you need:
for (Predicate rule : rules) {
boolean doesAnyMatch = persons.stream()
.anyMatch(rule);
...
}
findAny
You can substitute anyMatch
by a combination of filter
and findAny
to receive an actual match, i.e. a Person
, instead of just a boolean
:
Person matchingPerson = persons.stream()
.filter(rule)
.findAny();
How to compare objects by multiple fields
You can implement a Comparator
which compares two Person
objects, and you can examine as many of the fields as you like. You can put in a variable in your comparator that tells it which field to compare to, although it would probably be simpler to just write multiple comparators.
comparing multiple fields of n objects for common values
Here is a way to do it using Java 8 streams:
public class Test8 {
public static void main(String[] args) {
MyObj[] testData = {
new MyObj(1, "X", 3.14, "Larry"),
new MyObj(2, "X", 3.14, "Curly"),
new MyObj(3, "X", 3.14, "Moe"),
};
System.out.println("A = " + findCommon(testData, MyObj::getA).orElse(null));
System.out.println("B = " + findCommon(testData, MyObj::getB).orElse(null));
System.out.println("C = " + findCommon(testData, MyObj::getC).orElse(null));
System.out.println("D = " + findCommon(testData, MyObj::getD).orElse(null));
}
private static <T, R> Optional<R> findCommon(T[] data, Function<? super T, ? extends R> getterMethod) {
Set<R> values = Arrays.stream(data)
.map(getterMethod)
.collect(Collectors.toSet());
return (values.size() == 1
? Optional.ofNullable(values.iterator().next())
: Optional.empty());
}
}
class MyObj {
private final int a;
private final String b;
private final double c;
private final String d;
MyObj(int a, String b, double c, String d) {
this.a = a;
this.b = b;
this.c = c;
this.d = d;
}
public int getA() {
return this.a;
}
public String getB() {
return this.b;
}
public double getC() {
return this.c;
}
public String getD() {
return this.d;
}
}
Output
A = null
B = X
C = 3.14
D = null
UPDATE
Using Set
is an easy solution, but it always has to scan all objects.
If you have many objects, it may be good for performance to stop scanning as soon as you find differing values:
private static <T, R> Optional<R> findCommon(T[] data, Function<? super T, ? extends R> getterMethod) {
R onlyValue = null;
for (T obj : data) {
R value = getterMethod.apply(obj);
if (value == null)
return Optional.empty();
if (onlyValue == null)
onlyValue = value;
else if (! value.equals(onlyValue))
return Optional.empty();
}
return Optional.ofNullable(onlyValue);
}
Related Topics
Create Http Post Request and Receive Response Using C# Console Application
Non-Blocking Read from Standard I/O in C#
C# Open File, Path Starting with %Userprofile%
Convert.Toint32() a String with Commas
Is This a Covariance Bug in C# 4
Get the Application Pool Identity Programmatically
Dynamically Create a Generic Type for Template
Selecting Attribute Values with HTML Agility Pack
Missing Providername When Debugging Azurefunction as Well as Deploying Azure Function
Top Level Domain from Url in C#
Foo.Cmd Won't Output Lines in Process (On Website)
Canadian Postal Code Validation
How to Simply 'Read' a File That Is in Use
Is Using an an 'Async' Lambda with 'Task.Run()' Redundant
How to Segment the Elements Iterated Over in a Foreach Loop
Return All Enumerables with Yield Return at Once; Without Looping Through