I have a Java class Parent with 20 attributes (attrib1, attrib2 .. attrib20) and its corresponding getters and setters. Also I have two lists of Parent objects: list1 and list2.
Now I want to merge both lists and avoid duplicate objects based on attrib1 and attrib2.
Using Java 8:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.distinct()
.collect(Collectors.toList());
But in which place I have to specify the attributes? Should I override hashCode and equals method?
If you want to implement equals and hashCode, the place to do it is inside the class Parent. Within that class add the methods like
#Override
public int hashCode() {
return Objects.hash(getAttrib1(), getAttrib2(), getAttrib3(),
// …
getAttrib19(), getAttrib20());
}
#Override
public boolean equals(Object obj) {
if(this==obj) return true;
if(!(obj instanceof Parent)) return false;
Parent p=(Parent) obj;
return Objects.equals(getAttrib1(), p.getAttrib1())
&& Objects.equals(getAttrib2(), p.getAttrib2())
&& Objects.equals(getAttrib3(), p.getAttrib3())
// …
&& Objects.equals(getAttrib19(), p.getAttrib19())
&& Objects.equals(getAttrib20(), p.getAttrib20());
}
If you did this, distinct() invoked on a Stream<Parent> will automatically do the right thing.
If you don’t want (or can’t) change the class Parent, there is no delegation mechanism for equality, but you may resort to ordering as that has a delegation mechanism:
Comparator<Parent> c=Comparator.comparing(Parent::getAttrib1)
.thenComparing(Parent::getAttrib2)
.thenComparing(Parent::getAttrib3)
// …
.thenComparing(Parent::getAttrib19)
.thenComparing(Parent::getAttrib20);
This defines an order based on the properties. It requires that the types of the attributes itself are comparable. If you have such a definition, you can use it to implement the equivalent of a distinct(), based on that Comparator:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.filter(new TreeSet<>(c)::add)
.collect(Collectors.toList());
There is also a thread-safe variant, in case you want to use it with parallel streams:
List<Parent> result = Stream.concat(list1.stream(), list2.stream())
.filter(new ConcurrentSkipListSet<>(c)::add)
.collect(Collectors.toList());
For example:
public class Parent {
public int no;
public String name;
#Override
public int hashCode() {
return (no << 4) ^ name.hashCode();
}
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Parent))
return false;
Parent o = (Parent)obj;
return this.no == o.no && this.name.equals(o.name);
}
}
Override the equals and hashCode methods in Parent class to avoid duplicates from the lists. This will give you the exact result what you want.
If you want to override .equals(…) and .hashCode(), you need to do so on the Parent class. Note that this may cause other uses of Parent to fail. Alexis C.'s linked solution is more conservative.
Related
I want to check whether a List contains an object that has a field with a certain value. Now, I could use a loop to go through and check, but I was curious if there was anything more code efficient.
Something like;
if(list.contains(new Object().setName("John"))){
//Do some stuff
}
I know the above code doesn't do anything, it's just to demonstrate roughly what I am trying to achieve.
Also, just to clarify, the reason I don't want to use a simple loop is because this code will currently go inside a loop that is inside a loop which is inside a loop. For readability I don't want to keep adding loops to these loops. So I wondered if there were any simple(ish) alternatives.
Streams
If you are using Java 8, perhaps you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().filter(o -> o.getName().equals(name)).findFirst().isPresent();
}
Or alternatively, you could try something like this:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().map(MyObject::getName).filter(name::equals).findFirst().isPresent();
}
This method will return true if the List<MyObject> contains a MyObject with the name name. If you want to perform an operation on each of the MyObjects that getName().equals(name), then you could try something like this:
public void perform(final List<MyObject> list, final String name){
list.stream().filter(o -> o.getName().equals(name)).forEach(
o -> {
//...
}
);
}
Where o represents a MyObject instance.
Alternatively, as the comments suggest (Thanks MK10), you could use the Stream#anyMatch method:
public boolean containsName(final List<MyObject> list, final String name){
return list.stream().anyMatch(o -> name.equals(o.getName()));
}
You have two choices.
1. The first choice, which is preferable, is to override the `equals()` method in your Object class.
Let's say, for example, you have this Object class:
public class MyObject {
private String name;
private String location;
//getters and setters
}
Now let's say you only care about the MyObject's name, that it should be unique so if two `MyObject`s have the same name they should be considered equal. In that case, you would want to override the `equals()` method (and also the `hashcode()` method) so that it compares the names to determine equality.
Once you've done this, you can check to see if a Collection contains a MyObject with the name "foo" by like so:
MyObject object = new MyObject();
object.setName("foo");
collection.contains(object);
However, this might not be an option for you if:
You are using both the name and location to check for equality, but you only want to check if a Collection has any `MyObject`s with a certain location. In this case, you've already overridden `equals()`.
`MyObject` is part of an API that you don't have liberty to change.
If either of these are the case, you'll want option 2:
2. Write your own utility method:
public static boolean containsLocation(Collection<MyObject> c, String location) {
for(MyObject o : c) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Alternatively, you could extend ArrayList (or some other collection) and then add your own method to it:
public boolean containsLocation(String location) {
for(MyObject o : this) {
if(o != null && o.getLocation.equals(location)) {
return true;
}
}
return false;
}
Unfortunately there's not a better way around it.
This is how to do it using Java 8+ :
boolean isJohnAlive = list.stream().anyMatch(o -> "John".equals(o.getName());
Google Guava
If you're using Guava, you can take a functional approach and do the following
FluentIterable.from(list).find(new Predicate<MyObject>() {
public boolean apply(MyObject input) {
return "John".equals(input.getName());
}
}).Any();
which looks a little verbose. However the predicate is an object and you can provide different variants for different searches. Note how the library itself separates the iteration of the collection and the function you wish to apply. You don't have to override equals() for a particular behaviour.
As noted below, the java.util.Stream framework built into Java 8 and later provides something similar.
Collection.contains() is implemented by calling equals() on each object until one returns true.
So one way to implement this is to override equals() but of course, you can only have one equals.
Frameworks like Guava therefore use predicates for this. With Iterables.find(list, predicate), you can search for arbitrary fields by putting the test into the predicate.
Other languages built on top of the VM have this built in. In Groovy, for example, you simply write:
def result = list.find{ it.name == 'John' }
Java 8 made all our lives easier, too:
List<Foo> result = list.stream()
.filter(it -> "John".equals(it.getName())
.collect(Collectors.toList());
If you care about things like this, I suggest the book "Beyond Java". It contains many examples for the numerous shortcomings of Java and how other languages do better.
Binary Search
You can use Collections.binarySearch to search an element in your list (assuming the list is sorted):
Collections.binarySearch(list, new YourObject("a1", "b",
"c"), new Comparator<YourObject>() {
#Override
public int compare(YourObject o1, YourObject o2) {
return o1.getName().compareTo(o2.getName());
}
});
which will return a negative number if the object is not present in the collection or else it will return the index of the object. With this you can search for objects with different searching strategies.
Map
You could create a Hashmap<String, Object> using one of the values as a key, and then seeing if yourHashMap.keySet().contains(yourValue) returns true.
Eclipse Collections
If you're using Eclipse Collections, you can use the anySatisfy() method. Either adapt your List in a ListAdapter or change your List into a ListIterable if possible.
ListIterable<MyObject> list = ...;
boolean result =
list.anySatisfy(myObject -> myObject.getName().equals("John"));
If you'll do operations like this frequently, it's better to extract a method which answers whether the type has the attribute.
public class MyObject
{
private final String name;
public MyObject(String name)
{
this.name = name;
}
public boolean named(String name)
{
return Objects.equals(this.name, name);
}
}
You can use the alternate form anySatisfyWith() together with a method reference.
boolean result = list.anySatisfyWith(MyObject::named, "John");
If you cannot change your List into a ListIterable, here's how you'd use ListAdapter.
boolean result =
ListAdapter.adapt(list).anySatisfyWith(MyObject::named, "John");
Note: I am a committer for Eclipse ollections.
Predicate
If you dont use Java 8, or library which gives you more functionality for dealing with collections, you could implement something which can be more reusable than your solution.
interface Predicate<T>{
boolean contains(T item);
}
static class CollectionUtil{
public static <T> T find(final Collection<T> collection,final Predicate<T> predicate){
for (T item : collection){
if (predicate.contains(item)){
return item;
}
}
return null;
}
// and many more methods to deal with collection
}
i'm using something like that, i have predicate interface, and i'm passing it implementation to my util class.
What is advantage of doing this in my way? you have one method which deals with searching in any type collection. and you dont have to create separate methods if you want to search by different field. alll what you need to do is provide different predicate which can be destroyed as soon as it no longer usefull/
if you want to use it, all what you need to do is call method and define tyour predicate
CollectionUtil.find(list, new Predicate<MyObject>{
public boolean contains(T item){
return "John".equals(item.getName());
}
});
Here is a solution using Guava
private boolean checkUserListContainName(List<User> userList, final String targetName){
return FluentIterable.from(userList).anyMatch(new Predicate<User>() {
#Override
public boolean apply(#Nullable User input) {
return input.getName().equals(targetName);
}
});
}
contains method uses equals internally. So you need to override the equals method for your class as per your need.
Btw this does not look syntatically correct:
new Object().setName("John")
If you need to perform this List.contains(Object with field value equal to x) repeatedly, a simple and efficient workaround would be:
List<field obj type> fieldOfInterestValues = new ArrayList<field obj type>;
for(Object obj : List) {
fieldOfInterestValues.add(obj.getFieldOfInterest());
}
Then the List.contains(Object with field value equal to x) would be have the same result as fieldOfInterestValues.contains(x);
Despite JAVA 8 SDK there is a lot of collection tools libraries can help you to work with, for instance:
http://commons.apache.org/proper/commons-collections/
Predicate condition = new Predicate() {
boolean evaluate(Object obj) {
return ((Sample)obj).myField.equals("myVal");
}
};
List result = CollectionUtils.select( list, condition );
I have a POJO/DTO class with multiple list attribute like
class Boo {
private List<Foo> foos;
private List<Integer> pointers;
}
I want to compare if both lists contain the same values ignoring the order of the lists. Is it possible to achieve this without opening the object and ordering the lists?
Help would be appreciated. Thanks in Advance
"I want to compare if both contains same values instead of the order of list."
There is not a universal equality operator. Sometimes you want compare objects by certain properties. Probably the canonical example could be comparing strings, sometimes "computer" is equal or not than "Computer" or "Vesterålen" is equal or not than "Vesteralen".
In Java, you can redefine the default equivalence relation between objects (modifying the default behavior!).
The object List use as default equivalence relation the default equivalence relation of the contained objects and checking that equality in order.
The following example ignore the elements order only in one property:
class My {
private final List<String> xs;
private final List<Integer> ys;
My(List<String> xs, List<Integer> ys) {
this.xs = xs;
this.ys = ys;
}
public List<Integer> getYs() {
return ys;
}
public List<String> getXs() {
return xs;
}
#Override
public int hashCode() {
return xs.hashCode() + 7 * ys.hashCode();
}
#Override
public boolean equals(Object obj) {
if(!(obj instanceof My))
return false;
My o = (My) obj;
return
// ignoring order
getXs().stream().sorted().collect(toList()).equals(o.getXs().stream().sorted().collect(toList()))
// checking order
&& getYs().equals(o.getYs());
}
}
public class Callme {
public static void main(String... args) {
My m1 = new My(asList("a", "b"), asList(1, 2));
My m2 = new My(asList("b", "a"), asList(1, 2));
My m3 = new My(asList("a", "b"), asList(2, 1));
System.out.println(m1.equals(m2));
System.out.println(m1.equals(m3));
}
}
with output
true
false
But I can't define YOUR required equivalence relation, for example I do not ignore if one list contains more elements than the other but maybe you wish (eg. to you is equal {a, b, a} than {b, a}).
So, define you equivalence relation for your object and override hashCode and equals.
This boils down to comparing the lists. If the order of the items is irrelevant anyways you might fare better using Set instead of List.
Your equals then would look like
public boolean equals(object other) {
//here be class and null checks
return foos.equals(other.foos) && pointers.equals(other.pointers);
}
If you cannot use Set - either because you can have the same item multiple times or because order matters - you have can do the same as above with a reciprocal containsAll() call. This still would not take duplicate entries into consideration but will work quite fine otherwise.
You state that you cannot edit the class Boo. One solution would be to have a service class which does this for you a bit similar to Objects.equals().
class BooComparer {
public static bool equals(Boo a, Boo b) {
//again do some null checks here
return a.foos.containsAll(b.foos)
&& b.foos.containsAll(a.foos)
&& a.pointers.containsAll(b.pointers)
&& b.pointers.containsAll(a.pointers)
}
}
If this works for you - fine. Maybe you have to compare other members, too. And again: this will ignore if one of the lists has an entry twice.
I have a obj type MyObj as follow:
class MyObj{
String id;
String username;
String fullName;
String age;
//getters & setters
}
Suppose we have 2 lists containing different number of elements like so:
List<MyObj> listA
List<MyObj> listB
I have a generic method that detects elements from listA that are missing in listB:
public static <T> List<T> getListDifference(List<T> list1, List<T> list2) {
Collection<T> first = new HashSet<T>(list1);
Collection<T> second = new HashSet<T>(list2);
first.removeAll(second);
return new ArrayList<T>(first);
}
If objects from listA and listB has the same fields for all items, everything works just fine.
The problem is that some items has only id and username but others can have fullName or age too, and as result this method doesn't work anymore. I'd like to keep the same logic, considering only id field because it's present in all objects.
One obvious method is to copy only object's id field in other List<String> and work with obtained lists to detect elements, then just search for obj from both lists by id. This method has a big complexity, because of multiple iterations. Is there a short way to achieve this?
As # Kevin Esche mentioned, you have to implement equals() and hashCode() in your MyObj POJO, and according to your situation, they should be:
#Override
public int hashCode() {
return Objects.hash(id);
}
#Override
public boolean equals(Object obj) {
if (obj == this) return true;
if (!(obj instanceof MyObj)) return false;
MyObj myObj= (MyObj) obj;
return Objects.equals(id, myObj.id);
}
That will work even if ages are different and the ids are the same for example.
override MyObjs equals method:
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyObj myObj = (MyObj) o;
return (id != null ? !id.equals(myObj.id) : myObj.id != null);
}
IntelliJ/Android Studio have helpers for automatically generating equals and hashCode where you can choose the necessary properties.
You have to implement equals and hashCode to work with sets. Sets needs a way of recognising if one object is equal to another, and default behaviour is not gonna work here.
By the way, I suggest using retainAll method on a list, check this out.
I have just started using google's Guava collection (ComparisonChain and Objects). In my pojo I am overiding the equals method, so I did this first:
return ComparisonChain.start()
.compare(this.id, other.id)
.result() == 0;
However, I then realized that I could also use this :
return Objects.equal(this.id, other.id);
And I fail to see when comparison chain would be better as you can easily add further conditions like so:
return Objects.equal(this.name, other.name)
&& Objects.equal(this.number, other.number);
The only benefit I can see if you specifically need an int returned. It has two extra method calls (start and result) and is more complex to a noob.
Are there obvious benefits of ComparisonChain I missing ?
(Yes, I am also overriding hashcode with appropriate Objects.hashcode())
ComparisonChain allow you to check whether an object is less-than or greater-than another object by comparing multiple properties (like sorting a grid by multiple columns).
It should be used when implementing Comparable or Comparator.
Objects.equal can only check for equality.
ComparisonChain is meant to be used in helping objects implement the Comparable or Comparator interfaces.
If you're just implementing Object.equals(), then you're correct; Objects.equal is all you need. But if you're trying to implement Comparable or Comparator -- correctly -- that is much easier with ComparisonChain than otherwise.
Consider:
class Foo implements Comparable<Foo> {
final String field1;
final int field2;
final String field3;
public boolean equals(#Nullable Object o) {
if (o instanceof Foo) {
Foo other = (Foo) o;
return Objects.equal(field1, other.field1)
&& field2 == other.field2
&& Objects.equal(field3, other.field3);
}
return false;
}
public int compareTo(Foo other) {
return ComparisonChain.start()
.compare(field1, other.field1)
.compare(field2, other.field2)
.compare(field3, other.field3)
.result();
}
}
as opposed to implementing compareTo as
int result = field1.compareTo(other.field2);
if (result == 0) {
result = Ints.compare(field2, other.field2);
}
if (result == 0) {
result = field3.compareTo(other.field3);
}
return result;
...let alone the trickiness of doing that correctly, which is higher than you'd guess. (I have seen more ways to mess up compareTo than you can imagine.)
In the context of overriding methods in your POJOs, I think of a few of Guava's tools matching with a few standard methods.
Object.equals is handled using Objects.equals in roughly the manner you mentioned
Object.hashCode is handled with Objects.hashCode like return Objects.hashCode(id, name);
Comparable.compareTo is handled with ComparisonChain as below:
public int compareTo(Chimpsky chimpsky) {
return ComparisonChain.start()
.compare(this.getId(), chimpsky.getId())
.compare(this.getName(), chimpsky.getName())
.result();
}
I would be careful when using Guava's ComparisonChain because it creates an instance of it per element been compared so you would be looking at a creation of N x Log N comparison chains just to compare if you are sorting, or N instances if you are iterating and checking for equality.
I would instead create a static Comparator using the newest Java 8 API if possible or Guava's Ordering API which allows you to do that, here is an example with Java 8:
import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;
private static final Comparator<DomainObject> COMPARATOR=Comparator
.comparingInt(DomainObject::getId)
.thenComparing(DomainObject::getName,nullsLast(naturalOrder()));
#Override
public int compareTo(#NotNull DomainObject other) {
return COMPARATOR.compare(this,other);
}
Here is how to use the Guava's Ordering API: https://github.com/google/guava/wiki/OrderingExplained
I'm wondering what collection I should use for this purpose:
Requirements
Must contain tuples <value1,value2>
There is not relation between those values (no key-value pairs)
Can only contain unique tuples
<value1,value2> is equal to <value2,value1>
What would be best to use here?
Use any Set (HashSet, for instance). Create an object to represent your tuple and implement hashcode and equals properly.
Implement your own tuple class with equals and hashCode as outlined below, then use Set:
public class Tuple<T> {
T v1;
T v2;
#override
public boolean equals(Object o) {
if (o == null) {
return false;
}
if (o instanceof Tuple) {
return (v1.equals(o.v1) && v2.equals(o.v2))
|| (v1.equals(o.v2) && v2.equals(o.v1));
}
return false;
}
#override
public int hashCode() {
// must produce a.hashCode() == b.hashCode() if a.equals(b)
// example below may or may not work for your concrete equals()
return v1.hashCode() ^ v2.hashCode();
}
}
A Set appears to meet your criteria. The Set would have to contain another collection or a custom object that contains the two values.