I have a class defined as
class Book{
String author;
String title;
int id;
public boolean equals(Object o){
return id == ((Book)o).id;
}
public int hashCode{...}
}
In most of the cases uniqueness of the Books is determined by id, hence works properly. In one particular case, I want to merge two lists based on author and title value. I cannot directly use a Set and add the second list to the Set as comparison will happen on ids and not on author/title value. Only way for me is to have two nested for loops to compare each object's value.
List<Book> list1=...;
List<Book> list2 = ...;
Iterator<Book> iterator = list1.iterator();
while(iterator.hasNext()){
Book b1 = iterator.next();
for(Book b2:list2){
if(b1.getAuthor().equals(b2.getAuthor()) && b1.getTitle().equals(b2.getTitle())){
iterator.remove();
}
}
}
list2.addAll(list1);
Is there any way where we can override the equals method as per the use case (similar to Comparator where we can change the sort algorithm)?
Instead just have customized equals method that will check the author value and somehow following works
set.addAll(list2);
You can do something liek that with closures, but it is not overriding as such. The problem you have is this is a O(N*M) time complexity which is not idea. A better approach is O(N) is
Map<String, Book> books = new LinkedHashMap<>();
for (Book book : list1) books.put(book.author+"/"+book.title, book);
for (Book book : list2) books.remove(book.author+"/"+book.title);
list2.addAll(books.values());
For closures, you need a few functions I couldn't find.
static class MapStream<K, V> {
final Map<K, V> map;
final Function<V, K> func;
MapStream(Iterable<V> values, Function<V, K> func) {
map = new LinkedHashMap<>();
this.func = func;
addAll(values);
}
private void addAll(Iterable<V> values) {
for (V value : values)
map.put(func.apply(value), value);
}
public MapStream<K, V> removeAll(Iterable<V> values) {
for (V value : values) {
map.remove(func.apply(value));
}
return this;
}
public Collection<V> values() {
return map.values();
}
}
public static <T> Function<T, String> and(Function<T, String> func1, Function<T, String> func2) {
return (T t) -> func1.apply(t) + "\uffff" + func2.apply(t);
}
public static void main(String... ignored) {
List<Book> list1 = new ArrayList<>();
List<Book> list2 = new ArrayList<>();
Function<Book, String> commonKey = and((Book b) -> b.author, (Book b) -> b.title);
list2.addAll(new MapStream<>(list1, commonKey).removeAll(list2).values());
}
You can see that with some support you can see something with closures.
In general you need to externalize equals and hashCode methods. Therefore you could have something like:
class MyModelClass {
private EqualsImpl<MyModelClass> equalsImpl;
public MyModelClass(EqualsImpl<MyModelClass> equalsImpl) {
super();
this.equalsImpl = equalsImpl;
}
@Override
public boolean equals(Object obj) {
return equalsImpl.equals(this, obj);
}
@Override
public int hashCode() {
return equalsImpl.hashCode(this);
}
}
interface EqualsImpl<C> {
public boolean equals(C obj1, Object obj2);
public int hashCode(C obj);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With