Need explanation of natural ordering in Java - java

Would you give an example of "choose the natural ordering inside the class."
I read Object Ordering in docs.oracle but it doesn't say how the natural ordering is decided in the case where there are multiple elements. If a class has elements such as name, age, address, phone number; which one gets picked to do the natural ordering?
Then, I read in a post here that one can decide the natural ordering. I am a Java newbie trying to tackle a homework assignment and taking my first stab at the collections framework.

Yes, you are right in saying that one can decide the natural ordering. You'd be implementing Comparable and writing your own implementation of the compareTo() method. In that method, you decide what trumps what.

I prefer Comparator classes for this, even though the implementation looks a bit more heavyweight. The reason is that, for some objects, it's not always clear what's being compared. Using .compareTo() (that is, implementing IComparable) doesn't make it as clear as an explicit Comparator does.
BasketballPlayer player1 = new BasketballPlayer("Smith", 2.02);
BasketballPlayer player2 = new BasketballPlayer("Jones", 1.95);
List<BasketballPlayer> playersByHeight = new ArrayList<BasketballPlayer>();
List<BasketballPlayer> playersByName = new ArrayList<BasketballPlayer>();
playersByHeight.add(player1);
playersByHeight.add(player2);
playersByName.add(player1);
playersByName.add(player2);
Collections.sort(playersByName, new Comparator<BasketballPlayer>() {
#Override
public int compare(BasketballPlayer o1, BasketballPlayer o2) {
return o1.getName().compareTo(o2.getName());
}
});
Collections.sort(playersByHeight, new Comparator<BasketballPlayer>() {
#Override
public int compare(BasketballPlayer o1, BasketballPlayer o2) {
return o1.getHeight().compareTo(o2.getHeight());
}
});
Here I use an anonymous class, but you could make the Comparators their own (named) class if you like.

Related

How can we instantiate comparator while using it as an argument in sorting?

Why we use new keyword before comparator while using it as a constructor in sorting as comparator is an interface so we cannot instantiate it?
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
});
That's because this code does not instantiate Comparator. As you said, that's not possible.
Instead, it is syntax sugar. It's short for:
// Yes, you can define a class inside a method.
class $AutoGeneratedName implements Comparator<Person> {
#Override public int compare(Person p1, Person p2) {
return p1.getAge() - p2.getAge();
}
}
Collections.sort(persons, new $AutoGeneratedName());
In other words, short for: Define a new class, which implements Comparator. Then, instantiate this class once right away. Resolve this entire expression as a reference to this newly created instance. This construct is called an anonymous inner class.
CAREFUL - this code is bad.
20 years ago, that code was mostly fine, except for one detail: using a - b in comparisons is dangerous for very large numbers, but presumably, given that this is about 'age', not going to be an issue. Still, bad form; return Integer.comparing(p1.getAge(), p2.getAge()) would be much better.
But since then, this is no longer needed. You can write the concept much shorter like so:
Collections.sort(persons, (a, b) -> Integer.compare(a.getAge(), b.getAge());
We can do even that much, much simpler, and more readably by using List#sort with Comparator.comparingInt.
persons.sort(Comparator.comparingInt(Person::getAge));
which does exactly what you think it does when you just read it like its english: It sorts the collection 'persons' by comparing a specific int - which int? The one you get when you invoke getAge() on the person method.
This last snippet is what you should be using instead.

Comparable and Comaprator interfaces

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.

How to switch sorting order in TreeSet

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

using multiple comparators in a java binarySearch

How do I use multiple comparators in a binarySearch in java...
I'm trying to sort a list of contestants which are sorted by name and their starting number.
The problem is if two contestants have the same name I get an IndexOutOfBoundsException so I want to do a secondary binarySearch using the starting number (which is unique) but still keeping them in the right order with names.
This is what I've got right now:
static void add(Contestant c){
int pos = Collections.binarySearch(byName, c, new ConNameCmp());
if (pos >= 0){
pos = Collections.binarySearch(byName, c, new ConStartCmp());
}
byName.add(-pos-1, c);
One Comparator only
Don't use two Comparators, use a single Comparator that compares both values:
public int compare(Foo a, Foo b){
// compare bar() values first
int result = a.bar().compareTo(b.bar());
// compare baz() values only if bar() values are different
if(result==0){
result = a.baz().compareTo(b.baz());
}
return result;
}
(In your case bar() is the name and baz() is the number).
Use Libraries
Creating Comparators this way is a lot easier if you use either Guava or Commons / Lang
Guava Versions:
#Override
public int compare(final Foo a, final Foo b){
return ComparisonChain
.start()
.compare(a.bar(), b.bar())
.compare(a.baz(), b.baz())
.result();
}
Commons / Lang Version:
#Override
public int compare(final Foo a, final Foo b){
return new CompareToBuilder()
.append(a.bar(), b.bar())
.append(a.baz(), b.baz())
.toComparison();
}
(Both of these versions won't fail if any of the values are null, my quick and dirty code above will)
Solve the Problem
I don't think you should do a Binary search in the first place, this seems very complicated.
Why don't you use a TreeSet with a custom comparator? Or Collections.sort(list, comparator)? (For both of these options you can use the comparators I showed earlier).
Also, you should think about letting your Contestant implement Comparable<Contestant>. That way you won't need to use an external Comparator. You can use the same logic as above in the compareTo() method, just replace one of the objects with this.
You might have already tried this, and this solution might not be available to you, but if you can change your "Contestant" class, you can make it extend the "java.lang.Comparable" interface and override Comparable#compareTo(Contestant) method so that it takes both the name and starting number into account. Afterwards, you'll be able to use the Collections.binarySearch(Collection<Contestant>, Contestant) method for your need.

Most efficient way to see if an ArrayList contains an object in Java

I have an ArrayList of objects in Java. The objects have four fields, two of which I'd use to consider the object equal to another. I'm looking for the most efficient way, given those two fields, to see if the array contains that object.
The wrench is that these classes are generated based on XSD objects, so I can't modify the classes themselves to overwrite the .equals.
Is there any better way than just looping through and manually comparing the two fields for each object and then breaking when found? That just seems so messy, looking for a better way.
Edit: the ArrayList comes from a SOAP response that is unmarshalled into objects.
It depends on how efficient you need things to be. Simply iterating over the list looking for the element which satisfies a certain condition is O(n), but so is ArrayList.Contains if you could implement the Equals method. If you're not doing this in loops or inner loops this approach is probably just fine.
If you really need very efficient look-up speeds at all cost, you'll need to do two things:
Work around the fact that the class
is generated: Write an adapter class which
can wrap the generated class and
which implement equals() based
on those two fields (assuming they
are public). Don't forget to also
implement hashCode() (*)
Wrap each object with that adapter and
put it in a HashSet.
HashSet.contains() has constant
access time, i.e. O(1) instead of O(n).
Of course, building this HashSet still has a O(n) cost. You are only going to gain anything if the cost of building the HashSet is negligible compared to the total cost of all the contains() checks that you need to do. Trying to build a list without duplicates is such a case.
*
() Implementing hashCode() is best done by XOR'ing (^ operator) the hashCodes of the same fields you are using for the equals implementation (but multiply by 31 to reduce the chance of the XOR yielding 0)
You could use a Comparator with Java's built-in methods for sorting and binary search. Suppose you have a class like this, where a and b are the fields you want to use for sorting:
class Thing { String a, b, c, d; }
You would define your Comparator:
Comparator<Thing> comparator = new Comparator<Thing>() {
public int compare(Thing o1, Thing o2) {
if (o1.a.equals(o2.a)) {
return o1.b.compareTo(o2.b);
}
return o1.a.compareTo(o2.a);
}
};
Then sort your list:
Collections.sort(list, comparator);
And finally do the binary search:
int i = Collections.binarySearch(list, thingToFind, comparator);
Given your constraints, you're stuck with brute force search (or creating an index if the search will be repeated). Can you elaborate any on how the ArrayList is generated--perhaps there is some wiggle room there.
If all you're looking for is prettier code, consider using the Apache Commons Collections classes, in particular CollectionUtils.find(), for ready-made syntactic sugar:
ArrayList haystack = // ...
final Object needleField1 = // ...
final Object needleField2 = // ...
Object found = CollectionUtils.find(haystack, new Predicate() {
public boolean evaluate(Object input) {
return needleField1.equals(input.field1) &&
needleField2.equals(input.field2);
}
});
If the list is sorted, you can use a binary search. If not, then there is no better way.
If you're doing this a lot, it would almost certainly be worth your while to sort the list the first time. Since you can't modify the classes, you would have to use a Comparator to do the sorting and searching.
Even if the equals method were comparing those two fields, then logically, it would be just the same code as you doing it manually. OK, it might be "messy", but it's still the correct answer
If you are a user of my ForEach DSL, it can be done with a Detect query.
Foo foo = ...
Detect<Foo> query = Detect.from(list);
for (Detect<Foo> each: query)
each.yield = each.element.a == foo.a && each.element.b == foo.b;
return query.result();
Is there any better way than just looping through and manually comparing the two fields for each object and then breaking when found? That just seems so messy, looking for a better way.
If your concern is maintainability you could do what Fabian Steeg suggest ( that's what I would do ) although it probably isn't the "most efficient" ( because you have to sort the array first and then perform the binary search ) but certainly the cleanest and better option.
If you're really concerned with efficiency, you can create a custom List implementation that uses the field in your object as the hash and use a HashMap as storage. But probably this would be too much.
Then you have to change the place where you fill the data from ArrayList to YourCustomList.
Like:
List list = new ArrayList();
fillFromSoap( list );
To:
List list = new MyCustomSpecialList();
fillFromSoap( list );
The implementation would be something like the following:
class MyCustomSpecialList extends AbstractList {
private Map<Integer, YourObject> internalMap;
public boolean add( YourObject o ) {
internalMap.put( o.getThatFieldYouKnow(), o );
}
public boolean contains( YourObject o ) {
return internalMap.containsKey( o.getThatFieldYouKnow() );
}
}
Pretty much like a HashSet, the problem here is the HashSet relies on the good implementation of the hashCode method, which probably you don't have. Instead you use as the hash "that field you know" which is the one that makes one object equals to the other.
Of course implementing a List from the scratch lot more tricky than my snippet above, that's why I say the Fabian Steeg suggestion would be better and easier to implement ( although something like this would be more efficient )
Tell us what you did at the end.
Maybe a List isn't what you need.
Maybe a TreeSet would be a better container. You get O(log N) insertion and retrieval, and ordered iteration (but won't allow duplicates).
LinkedHashMap might be even better for your use case, check that out too.
Building a HashMap of these objects based on the field value as a key could be worthwhile from the performance perspective, e.g. populate Maps once and find objects very efficiently
If you need to search many time in the same list, it may pay off to build an index.
Iterate once through, and build a HashMap with the equals value you are looking for as the key and the appropriate node as the value. If you need all instead of anyone of a given equals value, then let the map have a value type of list and build the whole list in the initial iteration.
Please note that you should measure before doing this as the overhead of building the index may overshadow just traversing until the expected node is found.
There are three basic options:
1) If retrieval performance is paramount and it is practical to do so, use a form of hash table built once (and altered as/if the List changes).
2) If the List is conveniently sorted or it is practical to sort it and O(log n) retrieval is sufficient, sort and search.
3) If O(n) retrieval is fast enough or if it is impractical to manipulate/maintain the data structure or an alternate, iterate over the List.
Before writing code more complex than a simple iteration over the List, it is worth thinking through some questions.
Why is something different needed? (Time) performance? Elegance? Maintainability? Reuse? All of these are okay reasons, apart or together, but they influence the solution.
How much control do you have over the data structure in question? Can you influence how it is built? Managed later?
What is the life cycle of the data structure (and underlying objects)? Is it built up all at once and never changed, or highly dynamic? Can your code monitor (or even alter) its life cycle?
Are there other important constraints, such as memory footprint? Does information about duplicates matter? Etc.
I would say the simplest solution would be to wrap the object and delegate the contains call to a collection of the wrapped class. This is similar to the comparator but doesn't force you to sort the resulting collection, you can simply use ArrayList.contains().
public class Widget {
private String name;
private String desc;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
}
public abstract class EqualsHashcodeEnforcer<T> {
protected T wrapped;
public T getWrappedObject() {
return wrapped;
}
#Override
public boolean equals(Object obj) {
return equalsDelegate(obj);
}
#Override
public int hashCode() {
return hashCodeDelegate();
}
protected abstract boolean equalsDelegate(Object obj);
protected abstract int hashCodeDelegate();
}
public class WrappedWidget extends EqualsHashcodeEnforcer<Widget> {
#Override
protected boolean equalsDelegate(Object obj) {
if (obj == null) {
return false;
}
if (obj == getWrappedObject()) {
return true;
}
if (obj.getClass() != getWrappedObject().getClass()) {
return false;
}
Widget rhs = (Widget) obj;
return new EqualsBuilder().append(getWrappedObject().getName(),
rhs.getName()).append(getWrappedObject().getDesc(),
rhs.getDesc()).isEquals();
}
#Override
protected int hashCodeDelegate() {
return new HashCodeBuilder(121, 991).append(
getWrappedObject().getName()).append(
getWrappedObject().getDesc()).toHashCode();
}
}

Categories