There are two (three) different types of ordering:
- A natural order behavior that is implemented by the Class itself
- A task-specific order behavior implemented in code
- A mix of both, because they're compatible.
Generally:
Comparable is a Generic Class, so you should actually use Comparable<T> (T being a class-type), in your example use it reflexively as Comparable<Node> so the compiler knows you have a comparable Node that you want to compare to another (comparable) Node.
Ordering Type 1: Comparable<T> is used when you have an natural ordering on an element, and the elements incidates its own ordering behaviour.
Your Code redone:
1: Nodes that can be compare to Ts, which usually is problematic:
public class Node<T extends Comparable<T>> implements Comparable<T> {
public Node<T> _next;
public T _data;
protected Node(final T data, final Node<T> next) {
_next = next;
_data = data;
}
@Override public String toString() {
return _data.toString();
}
@Override public int compareTo(final T pO) {
return _data.compareTo(pO);
}
} // end Node class
2: Node that can be compared to other nodes, and the T it is containing must be Comparable also:
This is the default implementation you would use in your case.
public class Node<T extends Comparable<T>> implements Comparable<Node<T>> {
public Node<T> _next;
public T _data;
protected Node(final T data, final Node<T> next) {
_next = next;
_data = data;
}
@Override public String toString() {
return _data.toString();
}
@Override public int compareTo(final Node<T> pO) {
return _data.compareTo(pO._data);
}
} // end Node class
Ordering Type 2: Using a task-specific ordering:
This is in contrast to the situation above. Here, it's not the element that dictates ordering, but the task at hand:
The code for the Class does not necessarily need to (but still can) specify an ordering implementation. But then the ordering is done on specific contents with a specific ordering Comparator:
import java.util.ArrayList;
import java.util.Collections;
public class MyClass {
public final int mAge;
public final String mName;
public final float mValue;
protected MyClass(final int pAge, final String pName, final float pValue) {
mAge = pAge;
mName = pName;
mValue = pValue;
}
@Override public String toString() {
return "MyClass [mAge=" + mAge + ", mName=" + mName + ", mValue=" + mValue + "]";
}
public static void main(final String[] args) {
final ArrayList<MyClass> list = new ArrayList<>();
list.add(new MyClass(12, "Chris", 13.7f));
list.add(new MyClass(14, "Anna", 18.7f));
list.add(new MyClass(33, "Bob", 3.7f));
printList("Unordered", list);
// sort by age
Collections.sort(list, (p, q) -> p.mAge - q.mAge);
printList("Sorted by age", list);
// sort by Name
Collections.sort(list, (p, q) -> p.mName.compareTo(q.mName));
printList("Sorted by name", list);
// sort by Value
Collections.sort(list, (p, q) -> (int) Math.signum(p.mValue - q.mValue));
printList("Sorted by value", list);
}
private static void printList(final String pMessage, final ArrayList<MyClass> pList) {
System.out.println(pMessage);
for (final MyClass item : pList) {
System.out.println(item);
}
System.out.println();
}
}
... and of course Type 3, where you mix both, i.e. have an implements Comparable<> somewhere in the mix and using that