I have a list of POJOs I need to sort somehow. I define a Comprator inside the POJO class and use it to sort the list.
Is the following way correct/best practice? Is there a better way to do it?
public class CompratorTest {
public static void main(String[] args) {
List<Person> people = List.of(
new Person("zoe", "saturday", 40),
new Person("luca", "red", 15),
new Person("boris", "vin", 54),
new Person("boris", "apple", 33),
new Person("boris", "apple", 70)
);
List<Person> sortedPeople =
people.stream()
.sorted((person, other) -> Person.COMPARATOR.compare(person, other))
.collect(Collectors.toList());
sortedPeople.forEach(System.out::println);
}
#Data
#AllArgsConstructor
static
class Person {
final static Comparator<Person> COMPARATOR =
Comparator.comparing((Person person) -> person.getName())
.thenComparing(person -> person.getSurname())
.thenComparing(person -> person.getAge());
String name;
String surname;
int age;
}
}
Output is correct, by the way.
EDIT
Adding a more classic way:
#Data
#AllArgsConstructor
static class Animal implements Comparable<Animal> {
String name;
String race;
#Override
public int compareTo(Animal other) {
if (this.name.equals(other.name)) {
return String.CASE_INSENSITIVE_ORDER.compare(this.race, other.race);
}
return String.CASE_INSENSITIVE_ORDER.compare(this.name, other.name);
}
}
Which one do you think is a better solution?
There's a substantial distinction between the use cases for Comparator and Comparable.
Implementing the Comparable interface is suitable for objects that have a natural order in your domain model. I'm not sure whether animals have a natural order, but if it is the case from the perspective of how your application model the animals, that's fine - that's the way to go. Otherwise, your class should not implement Comparable.
It's not something opinion-based, documentation clearly defines when these interfaces are intended to be used.
Comparable:
This interface imposes a total ordering on the objects of each class
that implements it. This ordering is referred to as the class's
natural ordering, and the class's compareTo method is referred to as
its natural comparison method.
Comparator:
Comparators can also be used to control the order of certain data structures (such as sorted sets or sorted maps), or to provide an ordering for collections of objects that don't have a natural ordering.
Another obvious distinction, that you can define as many flavors of comparators as you need. Which is handy when there's no one specific way to compare and sort the objects. And they must have more meaningful names than comparator.
Personally, I don't see a huge harm in defining a couple of comparators as public static final fields, as in your example. If you have a single class that manages the instances of this type - extract the comparators into that class, otherwise if these objects are ubiquitous and used in many places you can leave them right inside the POJO (that an opinion based part).
This is not opinion based: TL;DR implement Comparable:
semantically, this is what Interfaces are designed for: they express a contract enforced by an object, a behavior of the object: if the objects are serializable, then they should implement Serializable, if they are comparable, then they should implement Comparable, etc...
inheritance will work as expected and be more readable: if you define a Dog that extends Animal, you can implement comparison for Dog using the super implementation (i.e. a Dog is compared like any other Animal) or overriding the implementation to implement a behavior specific to Dog. The user of your Dog class simply calls instance.compareTo(...) without having to worry about what final static comparator she/he should call
users of your Animal API know they have to implement Comparable when adding their own animal to the inheritance tree
Related
It is generally said that comparator is used to have multiple sorting sequences of collection of objects while comparable is used to have single sorting sequence.
What is the use of comparator interface in java when it is possible to have multiple sorting sequences using comparable interface?
import java.util.*;
enum CompareValue {RollNo, Marks;}
class Student implements Comparable<Student> {
public int marks;
public int rollNo;
public static CompareValue comparator = CompareValue.RollNo;
Student (int marks, int rollNo) {
this.marks = marks;
this.rollNo = rollNo;
}
public int compareTo(Student s) {
switch (comparator) {
case RollNo:
return this.rollNo - s.rollNo;
case Marks:
return this.marks - s.marks;
}
return 0;
}
}
public class Test
{
public static void main (String[] args)
{
Student s1 = new Student(59, 103);
Student s2 = new Student(87, 102);
Student s3 = new Student(78, 101);
Student students[] = {s1, s2, s3};
Arrays.sort(students);
System.out.println("Student list sorted by rollno");
for (Student s:students) {
System.out.println(s.rollNo + " - " + s.marks);
}
Student.comparator = CompareValue.Marks;
System.out.println("Student list sorted by marks");
Arrays.sort(students);
for (Student s:students) {
System.out.println(s.rollNo + " - " + s.marks);
}
}
}
When your compareTo method has different behaviors based on the value of some static variable, you are basically introducing a global setting that controls the natural ordering of the Student class.
This could be confusing and counter intuitive to users of your class.
Besides, it makes the implementation of compareTo awkward, especially if you have more than two implementations, and each implementation depends on multiple instance variables.
Comparator is a much more suitable interface to supply multiple different comparisons for instances of the same class, each implementation having its own compare() logic.
When you have objects that do not implement comparable, but you would like to sort a collection consisting them, you would either have to extend them just to sort your collection or provide a comparator that compares them even though they are not comparable.
Or you might want to compare sort those objects in a different manner then their natural sort.
Imagine such an example.
String is an object that is comparable. Imagine you want to sort a collection of strings based on their hashCode instead of the string natural order. How would you do it without creating a comparator?
What you have shown there is indeed multiple sort orders using Comparable, but don't you think it's too much boiler plate code? Let's say if you have added a new field to the class called name, and now you want to sort by name. You'd have to:
add a new case to the enum
add a new case to the compareTo.
Another disadvantage of using the approach you showed is that it is not necessarily clear what this means:
Arrays.sort(student);
You would have to look through your code and check what value you have set the comparator.
Also, if I were using your class and I want to sort by something else, I would have to create a Comparator anyway, because I can't edit your class.
But if you use Comparator, you solve all of these problems:
Arrays.sort(students, Comparator.comparing(Student::getName));
Therefore, Comparable is only useful when there is one natural order, like dates and times for example.
If we look at the Comparable and Comparator interfaces and what they mean, everything will be clear.
Comparable:
This is an internal property of a JAVA class i.e. it assumes that whenever one uses the internal compareTo() method, one is using it for the specified object.
public int compareTo(T o);
Therefore, in implementation of this method we use this which is the current object and compare it to some other object of same type. These can be treated as defaults or use for natural ordering.
Like 1 comes before 2 and so on. This is the natural ordering.
Comparator:
This is property which actually is not tightly bound to the Java class itself. Comparators are used to actually provide a method to be used by some other services (like Collections.sort()) for achieving a particular goal.
int compare(T o1, T o2);
By this we mean, You can have multiple Comparators, providing different ways of achieving different goals wherein the actual service can pick any two objects and compare them.
This can be used to provide custom ordering, like using some equation we can come up with an ordering where f(1) actually comes after f(2) and so on. This equation will likely be achieving some order which solves a use-case.
I have a custom class where I have implemented both Comparable and Comparator interface. The sorting/comparison logic is opposite for the two.
consider the below class as an example:
class Test implements Comparable<Test>, Comparator<Test>{
private Integer field;
public Test(Integer field){this.field = field;}
#Override
public int compareTo(Test obj){
return this.field.compareTo(obj.field);
}
#Override
public int compare(Test t1, Test t2){
return -t1.compareTo(t2);
}
//impl of equals, hashCode and toString omitted for this example
}
So when I add objects of Test to a TreeSet by default it is sorting by the implementation of the Comparable which is understood as per the JDK source. So is there any flag/switch to switch to the sorting represented by the Comparable implementation?
I do not want to pass another Comparator to the TreeSet constructor.
There is a misconception on your side:
A Comparable class has objects that can be compared against each other (for example by a container that wants to sort them
A Comparator is the thing that compares two objects of some class.
There is no need for you to make your class implement both.
And worse: remember that code communicates intent: the idea that your class implements both interfaces, but in "opposite" ways, that is very much counter intuitive. It will simply confuse your readers, and can lead to all kinds of bugs, just because your code does something that few experienced java developers would expect it to do. Never write code that surprises your readers (in a bad way).
Instead note that you can simply create a TreeSet using Collections.reverseOrder() for example! In other words: the fact that you defined how to compare two objects of Test allows you to use a default (reversing) comparator already.
Long story short: avoid "inventing" "clever" tricks to work around framework behavior. Instead, learn how the framework "ticks", and adapt to that. Don't fight the tide, flow with it.
Using the same object as both Comparator and Comparable is quite atypical. You can achieve both sort orders using just one of the two interfaces.
With just Comparator:
//Test implements Comparator. reversed() changes order
new TreeSet(new Test().reversed());
With just Comparable:
//elements are Comparable. reverseOrder changes order
new TreeSet(Comparator.reverseOrder());
If you use Comparator.comparingInt(Test::getField).reversed()) then you don't need to implement your own comparation methods to the Test class.
Full example code:
static class Test {
private int field;
public Test(int field) {
this.field = field;
}
public int getField() {
return field;
}
}
public static void main(String[] args) {
Test test = new Test(5);
Test test2 = new Test(8);
TreeSet<Test> tests = new TreeSet<>(Comparator.comparingInt(Test::getField).reversed());
tests.add(test);
tests.add(test2);
for (Test t:tests)
System.out.println(t.getField());
}
Outputs:
8
5
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 6 years ago.
Improve this question
Is it right to overload method in Java interface like below. And then use the method required in your subclass? If not, is there a better way to do it, kindly suggest.
interface IEmployees{
public List<String> getEmployees(List<String> employees, List<String> departments);
public List<String> getEmployees(List<String> employees, String name);
}
class EmployeesByDept implements IEmployees{
public List<String> getEmployees(List<String> employees, List<String> departments){
// select employees belonging to depts in list and return.
}
public List<String> getEmployees(List<String> employees, String name){
throw new UnsupportedOperationException();
}
}
class EmployeesByName implements IEmployees{
public List<String> getEmployees(List<String> employees, List<String> departments){
throw new UnsupportedOperationException();
}
public List<String> getEmployees(List<String> employees, String name){
// select employees with name in list and return.
}
}
in my opinion overloading an interface this way is not a good idea, as this produces unnecessary/useless code at the implementing class.
I would therefore recommend to write 2 or 3 different interfaces like so
interface IEmployees {
}
interface IEmployeesByDept extends IEmployees {
public List<String> getEmployees(List<String> employees, List<String> departments);
}
interface IEmployeesByName extends IEmployees {
public List<String> getEmployees(List<String> employees, String name);
}
This Way you can cleanly implement the interface that matches your case.
Well, yes and no, it depends.
In general there are most certainly cases for it, where appropriate.
In your case though, probably not, and a lot of your issue comes down to more fundamental design problems. So just by reading your snippet it appears the possibilities are that an IEmployees is:
A set of all employees in a system (for example, all of the employees that work on a project or for a company), or
An arbitrary list of employees (includes 1 but also e.g. search results, etc., the semantic equivalent of a List<Employee>, or
A construct that has a list of employees associated with it (for example, a project, or an organization).
But you say:
it is actually an employee filter class. It will be inherited by other filter classes and will implement any one of the overloaded methods in the interface.
So your first minor problem is the interface name itself. IEmployees leaves a lot up in the air, but if you name it something more self-documenting and descriptive, e.g. IEmployeeFilter, then things start to come together a little more obviously.
So now you have a "filter", and it appears you are trying to have multiple separate filter strategies:
By department
By employee name
Possibly others
These are separate filters, and you state your interface defines a filter, and therefore these are more appropriately organized as two separate subclasses.
So first of all the interface should be what is common to all filters. How the filtering is done is not the common aspect. The filtering itself is. So consider:
interface IEmployeeFilter {
public List<String> getEmployees (List<String> employees);
}
Now you have a filter that makes sense, a single common method, and everything else falls into place, e.g.:
class EmployeeNameFilter implements IEmployeeFilter {
private String name;
public EmployeeNameFilter (String name) {
this.name = name;
}
#Override
public List<String> getEmployees (List<String> employees) {
return employees filtered appropriately
}
}
And:
class EmployeeDepartmentFilter implements IEmployeeFilter {
private List<String> departments;
public EmployeeDepartmentFilter (List<String> departments) {
departments = new ArrayList<String>(departments);
}
#Override
public List<String> getEmployees (List<String> employees) {
return employees filtered appropriately
}
}
Etc. Then when you're ready to use one the interface is always the same:
List<String> employees = ...;
IEmployeeFilter filter = new EmployeeNameFilter("bob"); // or...
// IEmployeeFilter filter = new EmployeeDepartmentFilter(...);
List<String> results = filter.getEmployees(employees); // <- interface always the same
Point is, interfaces exist as a tool to make a job easier. When you run into a situation where you have a bunch of classes implementing that interface but they all implement different parts of it, you're starting to defeat the purpose of an interface, and it's a good hint that there's a fundamental change that needs to be made in your design.
That is, a more general rule of thumb can be: If your interface is making your job harder, or making your code more complicated, you've done something wrong. Otherwise, you've done something right.
Hope that made sense.
Your use-case does not warrant the use of an Interface at all
An Interface represents a contract. You defined the contract as being able to fulfill two requirements. If you know you won't be able to provide both parts of the contract, do not use an interface at all.
Additionally, I strongly doubt you have alternative ways of getting these Employees Lists, another reason not to use interfaces.
Alternative solution
I am guessing your IEmployeesXXX classes have no state variable. This is a good indicator that the methods are utility methods which fetch and return a list of objects.
You should make it a classic Utility class, i.e. an abstract final class with static methods.
Here's an example using your own code, wich becomes much cleaner:
public abstract final class Employeesutility{
public static List<String> getEmployees(List<String> employees, List<String> departments){
// select employees belonging to depts in list and return.
}
public static List<String> getEmployees(List<String> employees, String name){
// select employees with name in list and return.
}
}
Notes going forward
I do not like Utility classes much, I would rather make an intermediate class with rich internal representation, and rich collections wich expose your proposed interface methods. But for your use case, this would probably mean duplicate the entire DB (guessing you have one), which would be stupid. Please consider it if ever you decide to create a true Employee class... which you will when your program becomes big enough.
I have a custom object like this :
Linkedlist<ClassInfo> classes = new LinkedList<ClassInfo>();
Within that, there are accessors for a teacher's name, the class name, the room number, etc. These are all Strings. I have run into a situation where the data in that LinkedList needs to displayed by different parameters (i.e. teacher name, class name, the room number, etc.).
Can anyone supply a quick implementation of how to do this? If I use the Compartor interface, how would I be able tell it which String field to sort the list by? My research also lead me to the Collator, and I was wondering if this would be of use.
Appreciate any help.
Write a different Comparator implementation for each field:
Comparator<ClassInfo> CLASS_NAME_COMPARATOR = new Comparator<ClassInfo>() {
public int compare(ClassInfo class1, ClassInfo class2) {
return class1.getClassName().compareTo(class2.getClassName());
}
};
... // implementations for other fields
...and then sort by whichever comparator is appropriate:
Collections.sort(classes, CLASS_NAME_COMPARATOR);
You will have to provide a custom comparator for every ordering you need to sort your collection to. Eg:
class TeacherComparator implements Comparator<ClassInfo> {
public int compare(ClassInfo c1, ClassInfo c2) {
String teacher1 = c1.getTeacher();
String teacher2 = c2.getTeacher();
return teacher1.compareTo(teacher2);
}
}
class ClassNameComparator implements Comparator<ClassInfo> {
...
}
I'm trying to weigh the pros and cons of using an EnumMap over a HashMap. Since, I will always be looking up using a String, it seems that a HashMap with a String key would be the right choice. However, an EnumMap seems like better design because it conveys my intent to restrict keys to a specific enumeration. Thoughts?
Here is a make-believe example, showing how I will be using the Map:
enum AnimalType { CAT, DOG }
interface Animal {}
class Cat implements Animal {}
class Dog implements Animal {}
public class AnimalFactory {
private static final Map<AnimalType, Animal> enumMap
= new EnumMap<AnimalType, Animal>(AnimalType.class);
// versus
private static final Map<String, Animal> stringMap
= new HashMap<String, Animal>();
static {
enumMap.put(AnimalType.CAT, new Cat());
enumMap.put(AnimalType.DOG, new Dog());
stringMap.put("CAT", new Cat());
stringMap.put("DOG", new Dog());
}
public static Animal create(String type) {
Animal result = enumMap.get(AnimalType.valueOf(type));
Animal result2 = stringMap.get(type);
return result;
}
}
Assume that the AnimalType enum and map will ONLY be used by the AnimalFactory to create animals and nowhere else.
Which Map should I use?
If all valid keys can be enumerated, I would use that as it ensures you are always working with a valid value.
It can also avoid confusion as String can be used for lots of things and is easy to turn an "Animal" string into a string used for something else. As enum types are not interchangable with other types in general (unless you use a common interface), there is less chance of error in coding.
If the set of possible keys is finite and known in advance (as your example/question suggest), then the enum is a perfect representation of this. As other said, the use of an enum ensures that no mistake will be made in the use of the key.
Furthermore, this implementation of Map is quite optimized, as the range of the keys is known in advance (as far as I knwow, the EnumMap uses an array of length numberOfEnums internally, indexed by the enum's ordinal).
So I would also recommend EnumMap.
Two (little) things to keep in mind though :
you won't be able to add specialized cases by inheritance (you cannot extend an enum, so no Maps of ANIMALS with specialized Maps of MAMMALS on the side for example)
when adding a member to your enum, if you add it "in the middle" of the other members you change the ordinals. As this info can be used by the EnumMap, this can prove problematic if you reload an EnumMap constructed from the old version of the enum (for example using Serialization)
Key Of Map should be unmodifiable and Unique and this can be guranteed using Enum.
Also managing it would be easier and less error prone as compare to managing String.
So Go For EnumMap.
Also as we have advanced enums, we can attach many other information and operations with the keys itself.
enum AnimalType {
Dog("I am dog and I hate cats", true),
CAT("I am cat and I love to eat rats", true),
RAT("I am a mouse and I love tearing human cloths apart", false) ;
private final String description;
private final boolean isHelpFullToHuman;
private AnimalType(String description , boolean isHelpFullToHuman) {
this.description = description;
this.isHelpFullToHuman = isHelpFullToHuman;
}
public boolean isHelpFullToHuman() {
return isHelpFullToHuman;
}
public String getDescription() {
return description;
}
}
First of all, all you keys are final immutable. You should definitely use EnumMap.
This is like Hash in Ruby:
options = { :font_size => 10, :font_family => "Arial" }
:font_size is the symbol in Ruby, the final static counterpart in Java.