I have a list of objects:
List<WorkflowError> workflowErrors = new List<WorkflowError>();
Of which I am wanting to sort alphabetically on the string field errorCode.
I know I have to use
Collections.sort(list,comparator)
and write a custom Comparator:
public class SortByErrorComparator implements Comparator<WorkflowError>
{
// Comparator logic
return 0;
}
I have seen examples of how to do this for a one dimensional list but I can't work out how to begin for a list of objects.
Any help would be appreciated.
You need to implement the compare method.
public class SortByErrorComparator implements Comparator<WorkflowError> {
public int compare(WorkflowError obj1, WorkflowError obj2) {
return obj1.getErrorCode().compareTo(obj2.getErrorCode());
}
}
And then, you can simply do:
Collections.sort(list, new SortByErrorComparator()) ;
In the Java world, generally we do it inline using anonymous inner classes as so:
Collections.sort(list, new Comparator<WorkflowError>() {
public int compare(WorkflowError obj1, WorkflowError obj2) {
return obj1.getErrorCode().compareTo(obj2.getErrorCode());
}
});
In your comparator, you have to explain how the specific object should be sorted.
if (obj.att1 < obj2.att2)
...
do you get it?
When sorting the list the sort implementation will use your comparator to compare individual paisr of objects. So what your comparator needs to do is decide, for a given pair of WorkFlowErrors, which comes 'first'.
from what you've said this sounds like just getting the error code from each one and doing a string compairson.
If you will always want to compare WorkflowError objects in this manner, the easiest way is to implement Comparable<WorkflowError> on WorkflowError itself.
public int compareTo(WorkflowError that) {
return this.getErrorCode().compareTo(that.getErrorCode());
}
That's assuming errorCode implements Comparable.
Related
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
I have an object of a custom type called Suffix which is defined as
class Suffix
{
int index;
String suff;
}
I create an Array of objects of type suffix
Suffix s[] = new Suffix[10];
I need to sort it according to the string suff data member. How can I do that? I am unable to understand most of the custom implementations available online.
eg :
If I have s[0].suff = "hats",s[1].suff = "ats", s[2].suff ="ts". Now I need to sort this array based on the suffix. ("ats",1) , ("hats",0) and ("ts",2) should be sorted order.
You can use Arrays.sort(..) if your object extends Comparable interface.
class Suffix implements Comparable<Suffix>
{
int index;
String suff;
#Override
public int compareTo(Suffix o) {
return this.suff.compareTo(o.suff);
}
}
You can use as below now
Suffix s[] = new Suffix[10];
Arrays.sort(s);
You got three possibilities:
Write your own sorting algorithm and compare the suff String there.
Implements the Comparable<> interface in your Suffix class, Override the comparteTo() method and use Arrays.sort(); which takes the array of Compareable objects as parameter.
Wirte a Comparator<> and use the Arrays.sort() method which takes the array and the Comparator.
The third way have one advantage. If you want to sort by index instead of suff param, you can pass another Comparator to the sort() method.
Suffix s[] = new Suffix[10];
Comparator comp = new Comparator<Suffix>(){
#Override
public int compare(Suffix arg0, Suffix arg1)
{
return arg0.suff.compareTo(arg1.suff);
}
};
Arrays.sort(s,comp);
You have nothing to change in your Suffix class. Hint: You can make an extra class with your Comparators as static fields.
Apart from using Comparable as #insert-username-here and #vinayknl said, The other one is using Comparator. Inside the method you want to sort add the following:
Comparator comparator = new Comparator<Suffix>() {
public int compare(Object o1, Object o2) {
Suffix s1 = (Suffix) o1;
Suffix s2 = (Suffix) o2;
return s1.getSuff().compareTo(s2.getSuff());
}
}
Array.sort(s, comparator);
Note: don't forget to add getter.
Check out the Comparable interface. This interface will allow your classes to be compared according to your own criteria.
Quick google search came up with this helpful page.
Can I define my own list in Java?
I have my own list-type class that is very similar to a LinkedList, called PersonList.
In another program, I'm using a Comparator, so I need to have a List() object as the parameter.
Is it possible to make the following statement, if I make changes in my code?
List list1= new PersonList();
PersonList doesn't extend or import anything.
You'd need to implement the built in interface java.util.List. It would need to define all the methods listed in the interface java.util.List.
You just have to overload the equals function which is implemented by every
class of Type Object (Every class). The list implementation will use your equals implementation due to the polymorphic concept of OOP.
I strongly recommend to use the given List implemenmtations because they meet all
performance issues you don't even think about. When you have concurrency issues refer to the documentation.
In order to achieve customized comparison you have to implement the Comparable interface
and implement its method toCompare(..);
In this way you can use all given Collection API classes and extend them using your own
comparison or equals algorithm which meets your application needs.
Update to to Comments
class Person implements Compareable {
#override
public int compareTo(Person p) {
return p.age > this.age; //Or whatever
}
#Override
equals(Object person) {
if (person instanceof Person) {
Person p = (Person)person;
if (p.x == this.x &&
p.y == this.y &&
p.address.equals(this.address) {
...
return true;
}
}
return false;
}
}
And now just intialize you list.
List<Person> personList = new ArrayList<Person>();
or
List<Persin> personList = new Vector<Person>();
or
LinkedList<Person> personList = new Queue<Person>();
and and and.
Collections.sort(personList);
To answer the question in the comment, "How would I go about writing my own Comparator for a Linked List?":
public class PersonListComparator implements Comparator<LinkedList> {
#Override
public int compare(LinkedList list1, LinkedList list2) {
// something that returns a negative value if list1<list2, 0 if list1 and
// list2 are equal, a positive value if list1>list2.
}
}
See the javadoc for Comparator, especially the text at the top. This explains what could happen if the compare function could return 0 when list1.Equals(list2) is false. It's not necessarily a problem, depending on how you use it.
Note that I'm still assuming you want to compare entire lists (rather than just individual Persons). Based on later comments, it looks like you want to compare Person objects, but provide different ways to compare ("depending on the different parameter being compared"). You could define more than one class that implements Comparator<Person>. Or you could define a class that takes a parameter when you construct the object:
public enum ComparisonType { NAME, AGE, WEIGHT } // whatever
public class ComparePerson implements Comparator<Person> {
private ComparisonType type;
public ComparePerson(ComparisonType type) {
this.type = type;
}
#Override
public int compare(Person p1, Person p2) {
switch(type) {
case NAME:
// return comparison based on the names
case AGE:
// and so on
...
}
}
}
I haven't tested this, so I could have made a mistake, but you get the idea. Hope this helps, but it's still possible I've misunderstood what you're trying to do.
I have a class which has a String field called name. I have an array of type SomeClass[] and I want to iterate through these SomeClass objects in String order on their names.
I'm curious as to what the most efficient way to do this would be. Should I use a comparator of some sort? Would it be a good idea to put them all into a TreeMap and then iterate through that or something similar? I'm sure I could come up with a solution, but I'm also sure that it would be less than efficient.
Any help is appreciated.
You can just Arrays.sort your Comparable class, like Arrays.sort(a) (see the code)
Or, if you wanted to use Collections framework
Arrays.asList(...) and Collections.sort(..) is the key.
IF SomeClass is like this
public class SomeClass implements Comparable<SomeClass>{
public String val;
#Override
public int compareTo(SomeClass that) {
return this.val.compareTo(that.val);
}
#Override
public String toString() {
return this.val;
}
}
yo can sort like this
SomeClass o = new SomeClass();
o.val = "z";
SomeClass t = new SomeClass();
t.val = "a";
SomeClass th = new SomeClass();
th.val = "m";
SomeClass[] a = new SomeClass[]{o, t, th};
//this
Arrays.sort(a);
//or this
List<SomeClass> l = Arrays.asList(a);
System.out.println(l);
Collections.sort(l);
System.out.println(l);
Put your Classes in an Arraylist and use its sort method.
Unverified code:
yourArray.sort(new YourNameComparator());
class YourNameComparator implements Comparator<YourNameClass> {
int compare(YourNameClass y1, YourNameClass y2) {
return y1.getName().compareTo(y2.getName());
}
}
In my opinion, your best bet would be to, as you say, put it in a TreeMap (or some data structure that sorts it for you) and then read it out already sorted. There's hardly a way to get faster, and this way would produce the cleanest and most readable code.
Your two options: have your class implement comparable and write a compareTo method or create a comparator and feed that into Collections.sort(List list, Comparator c)
Since you want to compare strings, you probably want to use comparable since StringY.compareTo(String x) already exists.
http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Comparable.html
http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Collections.html
Basically, I have a Container class called "Employees" which has in it an ArrayList. This ArrayList contains "Employee" objects, which in turn contain "EmployeeData" objects which in turn contain String objects such as "first" or "last" (which are employee names).
Here's a diagram of the ArrayList structure:
ArrayList[Employee] emps ==> 1:Many ==> Employee emp
Employee emp ==> 1:1 ==> EmployeeData data
EmployeeData data ==> 1:2 ==> String last // A string that contains employee's last name.
How in the world would I perform a quicksort on the ArrayList so that the "Employee" objects in it are in alphabetical order based on the String object "last"? It seems kinda complicated!
Here's a basic design of my classes:
class Employees{
//data:
private ArrayList<Employee> emps = new ArrayList<Employee>();
//Some constructors go here
//Methods to add, remove, toString, etc, go here
public /*output a sorted ArrayList?*/ sort(){
// Some kind of "quicksort" in here to modify or create a new ArrayList sorted by employee's las name...
}
}
class Employee{
//data:
EmployeeData data;
// Some methods to construct and modify EmployeeData data.
}
class EmployeeData{
//data:
String first, last; // I wish to sort with "last". How do you do it?
double payrate, hours;
//...methods...
}
As you can see, those are the classes. I have no idea how to implement "sort" in the "Employees" class so that it sorts the ArrayList by the "last" variable of the "EmployeeData" class.
You can make a comparator, something like:
public class MyComparator implements Comparator<Employee>
{
public int compare(Employee e1, Employee e2)
{
return e1.getData().getLast().compareTo(e2.getData().getLast());
}
}
Then use it to sort the list.
Collections.sort(myList, new MyComparator());
Alternatively, you can use a TreeSet to sort on insertion using this comparator or make the Employee a comparable object to sort using Collections or a SortedSet.
public class Employee implements Comperable<Employee>
{
...
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
...
}
Define Employee implements Comparable<Employee>.
In the compareTo method, dig into the layers and compare the strings you need. Then you can use Collections.sort(), or you can store the data in a SortedSet, which is naturally ordered.
The best practice is to encapsulate the sorting logic in the class stored in the ArrayList, Employee in this case. Implement Comparable by creating a compareTo(Employee) method.
import java.util.*;
public class Employee implements Comparable<Employee> {
public EmployeeData Data;
public Employee(String first, String last)
{
Data = new EmployeeData(first, last);
}
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
public String toString() {
return Data.First + " " + Data.Last;
}
public static void main(String[] args) throws java.io.IOException {
ArrayList list = new ArrayList();
list.add(new Employee("Andy", "Smith"));
list.add(new Employee("John", "Williams"));
list.add(new Employee("Bob", "Jones"));
list.add(new Employee("Abraham", "Abrams"));
Collections.sort(list);
for (int i = 0; i < list.size(); i++)
{
System.out.println(list.get(i));
}
System.in.read();
}
}
public class EmployeeData {
public String First;
public String Last;
public EmployeeData(String first, String last)
{
First = first;
Last = last;
}
}
Output:
Abraham Abrams
Bob Jones
Andy Smith
John Williams
Peter DeWeese and others have given you very good answers. You can use
Collections.sort(myList, new MyComparator());
to sort myList using a Comparator you have defined. <=== What the heck does that mean?
In Java, if something implements Comparable (java.lang.comparable) then you can define an order for your elements. It seems like you know what Java Generics are, as you used them to declare your ArrayList as being of type < Employee >. This is awesome, because you can store an Employee object into each entry in the ArrayList. So far so good?
However, if you want to sort objects, first you have to define an order. Since objects can have various properties, maybe I want to sort my employees by ear-size. In this case, I simply tell Java that my class implements Comparable. With generics, I have to specify that it implements Comparable< Employee > because I am defining an order for my Employee objects (peons, minions, whatever).
Peter DeWeese mentioned:
public int compareTo(Employee e)
{
return this.getData().getLast().compareTo(e.getData().getLast());
}
and Jason Goemaat mentioned:
public int compareTo(Employee other)
{
return Data.Last.compareTo(other.Data.Last);
}
What the heck does this mean? If I say that my class implements Comparable then I need to define a compareTo function. (An interface is a collection of methods that need to be implemented) The function compareTo defines the order of my elements.
From the Comparable< T> spec:
int compareTo(T o)
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
If I am comparing ear sizes, and let's say I want big ears to come first in my list, then I could (re)define compareTo as:
public int compareTo(Employee e)
{
if (this.earSize > e.earSize) //big ears come first
return -1;
if (this.earSize == e.earSize) //equality
return 0;
else
return 1; // if e.earSize > this.earSize then return 1
}
To answer Steve Kuo's question, we put the keyword this in our comparator because when we call the compareTo method
x.compareTo(y);
the keyword this will refer to x.
You can think of compareTo as being a method of the object x, so when you call x.compareTo(y) you are really saying this.compareTo(y) from within the scope of object x.
We can also look at a String example:
This means that if I want "Medvedev" to come before "Putin" (as 'M' comes before 'P' in the English alphabet) I would have to state that I want compareTo to return -1 when comparing Medvedev to Putin.
String TheMString = "Medvedev";
String ThePString = "Putin";
then the line
TheMString.compareTo(ThePString);
will evaluate to -1.
Now a standard routine such as Collections.sort(list, comparator) will be able to use these values that compareTo returns to figure out the [absolute] order of list. As you may know, sorting is a comparison based operation and we need to know what value is "less than" or "greater than" another value in order to have a meaningful sort.
One big caveat is that if you call compareTo on Strings, it defaults to alphabetical order, so you may simply tell compareTo to return A.compareto(B) and it will make sure the strings are in order.
Normally (well, I should say, in other cases) when redefining the compareTo method, you must explicitly state a neg/zero/pos return value.
I hope that helps.