Issue
I have two Set
s like the following:
Set<Book> s1 = createBooks();
Set<Book> s2 = createBooks(); // or any set of values
and this method:
private Set<Book> createBooks() {
List<String> name = Arrays.asList("book 1", "book 2", "book 3");
List<BookType> type = Arrays.asList(BookType.TYPEONE, BookType.TYPETWO);
Set<Book> listOfBooks = new HashSet<>();
for(int i=0; i<2; i++) {
Book book = new Book();
book.setName(name.get(i));
book.setType(type.get(i));
listOfBooks.add(books);
}
return listOfBooks;
}
And this class as well:
Class Book {
private String name;
private BookType type;
//here is getter and setter ...
}
I did implement this Comparator
, but I'm not sure if it's working properly:
Comparator<Book> comparingBooks = (o1, o2) -> {
if (o1.getName().compareTo(o2.getName()) == 0) {
return o1.getType().compareTo(o2.getType());
}
return o1.getName().compareTo(o2.getName());
};
//I did try this implementation but am not sure it's working as well:
//Comparator<Book> comparingBooks = Comparator.comparing((Book b)->b.getNName())
.thenComparing(b->b.getType());
An example of the sets when the expected result is true
:
s1 = (new Book("book 1", BookType.ONE), new Book("book 2", BookType.TWO));
s2 = (new Book("book 1", BookType.ONE), new Book("book 2", BookType.TWO));
An example of the sets when the expected result is false
:
s1 = (new Book("book 1", BookType.ONE), new Book("book 2", BookType.TWO));
s2 = (new Book("book 1", BookType.ONE), new Book("book 2", BookType.ONE));
I need to compare these sets with something like this:
// this flag should validate any difference of Name
// and Type by returning true,
// if both Sets have exactly the same objects,
// it should return false.
boolean compareBooks = s1.stream().anyMatch(a -> s2.stream().anyMatch(b -> comparingBooks.compare(a, b) != 0));
I did try this as well but didn't work and always return same value:
boolean compareBooks = SetUtils.isEqualSet(s1, s2);
I have no equals and hashcode implementation and can't implement that.
Is there any other way with java 8 and comparator to check and compare these sets?
Solution
A Fix for Comparator
In order to create a Comparator
in this case you need to provide generic type information explicitly, like that: <Book, String>comparing()
, where Book
and String
are the parameter type and return type of the Function
that Comparator.comparing()
expects as a parameter.
Without an explicit declaration, the compiler has not enough data to determine the type of the variable book
, and inside both comparing()
and thenComparing()
its type will be inferred as Object
.
Comparator<Book> comparingBooks =
Comparator.<Book, String>comparing(book -> book.getName())
.thenComparing(book -> book.getType());
If the case if only one of the static methods was used, the type of the book
variable will be correctly inferred by the compiler as Book
based on the type of the locale variable Comparator<Book> comparingBooks
.
Both method lambda expressions could be replaced with method references:
Comparator<Book> comparingBooks =
Comparator.<Book, String>comparing(Book::getName)
.thenComparing(Book::getType);
for information on the syntax of generic methods, take a look at this tutorial
for more information on how to build comparators with Java 8 methods take a look at this tutorial
Stream
should validate any difference of Name and Type by returning true, if both Sets have exactly the same objects, it should return false.
Stream s1.stream().anyMatch(a -> s2.stream().anyMatch(b -> ...))
is currently checking whether there's an element in the first set s1
that is different from ANY of the elements in s2. I.e. the nested anyMatch()
will return true
for the first element encountered in s1 that has not matching element in s2. After the comparison of Book("book 1", BookType.ONE)
(s1) with Book("book 2", BookType.TWO)
(s2) anyMatch()
terminates by returning true
. And enclosing anyMatch()
operation propagates this result.
Precisely the same will happen with the second example. The same pair of elements differ, although you've changed a type, names are not equal. Result is true
.
Note:
- that by convention classes are usually named with singular nouns, i.e.
Book
(not Books),Event
,Person
, etc. - as a rule of thumb, if your objects are intended to be used with collection they must implement
equals/hashCode
contract, your requirement is very unusual.
Answered By - Alexander Ivanchenko
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.