I have this class:
public class Sample implements Comparable<Sample> {
public String a;
public String b;
public String c;
public int compareTo (Sample sampleToCompare) {
int compResult = this.a.compareTo(sampleToCompare.a);
return (compResult != 0 ? compResult :
this.b.compareTo(sampleToCompare.b));
}
}
I want compareTo() to behave or sort using different class properties depending if a flag is set.
So, if flag == 1 I'd like compareTo() to using property c, otherwise is flag == 0, whatever is currently in the method.
In other words, sort the same class in different ways.
I am not sure how to achieve this. Please help.
Also, please let me know if more information is needed from my side.
If you want to implement different kind of sorting, you should take a look at java.util.Comparator interface.
public class SampleComparatorA implement Comparator<Sample> {
public int compare(Sample a, Sample b) {
// Your sorting
}
}
And use java.util.Collections.sort() method with the Comparator as the secound parameter instead.
Collections.sort(aSampleList, new SampleComparatorA());
How about:
public int compareTo(Sample sampleToCompare) {
if (flag == 1) {
return this.c.compareTo(sampleToCompare.c);
}
if (flag == 0) {
// current stuff
}
...
}
That's not a very object-oriented way to do it, though. Probably you should have two different comparators and a way to select them based on your "flag" value. Something like:
class Sample {
private String a;
private String b;
private String c;
}
class ASampleComparator implements Comparator<Sample> {
public int compare(Sample o1, Sample o2) {
return o1.a.compareTo(o2.a);
}
}
class BSampleComparator implements Comparator<Sample> {
public int compare(Sample o1, Sample o2) {
return o1.b.compareTo(o2.b);
}
}
class CSampleComparator implements Comparator<Sample> {
public int compare(Sample o1, Sample o2) {
return o1.c.compareTo(o2.c);
}
}
public Comparator<Sample> pickComparator(int flag) {
switch (flag) {
case 0:
return new ASampleComparator();
case 1:
return new BSampleComparator();
case 2:
return new CSampleComparator();
default:
throw new IllegalArgumentException("Bad flag value: " + flag);
}
}
You should make your flag static so the comparison will be consistent (as described in Effective Java, item 12), otherwise, you might get that a.compareTo(b) returns that a > b, but b.compareTo(a) returns that b > a. So the simplest implementation I can think about is:
public class Sample implements Comparable<Sample> {
public String a;
public String b;
public String c;
public static boolean my_flag = false;
public int compareTo (Sample sampleToCompare) {
if (flag) {
return this.c.compareTo(sampleToCompare.c);
}
int compResult = this.a.compareTo(sampleToCompare.a);
return (compResult != 0 ? compResult :
this.b.compareTo(sampleToCompare.b));
}
}
Related
Lets say I have a Product class in Java and 2 Comparators:
1st is price Comparator for asc order.
2nd is price Comparator for desc order.
It can be that if I changed the 1st to be product name Comparator, so, the 2nd will change automatic to name Comparator as well?
Thanks alot!
Exmaple:
class ProductComparatorByPriceDesc implements Comparator<Customer> {
#Override
public int compare(Product o1, Product o2) {
return o1.getPrice() - o2.getPrice();
}
}
Class ProductComparatorByPriceAsc implements Comparator<Customer> {
#Override
public int compare(Customer o1, Customer o2) {
return o2.getPrice() - o1.getPrice();
}
}
So if i changed the 1st comparator to sort by name, not price, the 2nd will changed as well, but not the opposite!
One way would be:
import java.util.*;
class SomeClass {
public int price;
public String name;
public SomeClass(String name, int price) {
this.name = name;
this.price = price;
}
}
class PriceOrNameComparator implements Comparator<SomeClass> {
boolean compareByPrice;
public PriceOrNameComparator byPrice() {
this.compareByPrice = true;
return this;
}
public PriceOrNameComparator byName() {
this.compareByPrice = false;
return this;
}
public int compare(SomeClass a, SomeClass b) {
if (compareByPrice) {
return a.price - b.price;
} else {
return a.name.compareTo(b.name);
}
}
public Comparator<SomeClass> reverseComparator() {
return new Comparator<SomeClass>() {
public int compare(SomeClass a, SomeClass b) {
int res = PriceOrNameComparator.this.compare(a, b);
if (res == 0) {
return 0;
} else {
return (res > 0) ? -1 : 1;
}
}
};
}
}
class Test {
public static void main(String[] args) {
SomeClass s1 = new SomeClass("a", 5);
SomeClass s2 = new SomeClass("b", 4);
PriceOrNameComparator c = new PriceOrNameComparator().byPrice();
Comparator<SomeClass> r = c.reverseComparator();
System.out.println(c.compare(s1, s2)); // 1
System.out.println(r.compare(s1, s2)); // -1
c.byName();
System.out.println(c.compare(s1, s2)); // -1
System.out.println(r.compare(s1, s2)); // 1
}
}
Basically, the outer comparator is configurable, and the inner, reverse order, comparator, being an anonymous inner class, has an implicit reference to the outer comparator and can observe changes in its state.
I would suggest only having a single comparator class for comparing by price, and a separate comparator class to compare by name (or no classes - see the end of my answer). Each class does one thing, and does it well.
Then you can reverse any comparator using the Comparator.reversed default method... and likewise you can chain them together using Comparator.thenComparing, should you wish to order by name and then price, for example:
Comparator<Product> nameThenPrice =
new NameComparator().thenComparing(new PriceComparator());
(If you're not using Java 8, it's easy enough to write a ReversingComparator which takes an existing one, and a CompoundComparator which takes two existing ones.)
You can also use Java 8's static methods in Comparator:
Comparator<Product> byName = Comparator.comparing(p -> p.getName());
Comparator<Product> byPrice = Comparator.comparing(p -> p.getPrice());
Comparator<Product> nameThenPrice = byName.thenComparing(byPrice);
That way you often don't need to implement Comparator at all manually.
I have an Enum with around 70 fields.
I want 10 of them to be displayed in a particular order, then I want the rest to be displayed alphabetically using a Comparator. I have tried many things, but I can't get it to work.
Here is a sample enum with reduces attributes
I want Picard, Worf and William to display first, then the rest alphabetically
I cannot use any third libraries. It must be java core. So if you want to provide guava answers, or apache commons answer, please do so in addition to java core.
public enum StarTrek {
JeanLucPicard("Picard"),
GeordiLaForge("Geordi"),
DiannaTroi("Dianna"),
Worf("Worf"),
WilliamRiker("William"),
Q("Q");
private String label;
StarTrek(String label) { this.label = label; }
#Override public String toString() { return label; }
}
List<StarTrek> specificOrder = Arrays.asList(StarTrek.JeanLucPicard, StarTrek.Worf, StarTrek.WilliamRiker);
Comparator<StarTrek> comp = new Comparator<StarTrek>() {
#Override
public int compare(StarTrek o1, StarTrek o2) {
//TODO: loop through the specific order, and display those first, then for the rest, go alphabetic
return 0;
}
};
List<StarTrek> all = Arrays.asList(StarTrek.values());
Collections.sort(all, comp);
It is bad design to place additional data in your enum just for the purposes of displaying in a particular order. Instead, place all that logic in your Comparator, as shown below:
public class StarTrekSorter implements Comparator<StarTrek> {
private static final List<StarTrek> ORDERED_ENTRIES = Arrays.asList(
StarTrek.JeanLucPicard, StarTrek.Worf, StarTrek.WilliamRiker);
#Override
public int compare(StarTrek o1, StarTrek o2) {
if (ORDERED_ENTRIES.contains(o1) && ORDERED_ENTRIES.contains(o2)) {
// Both objects are in our ordered list. Compare them by
// their position in the list
return ORDERED_ENTRIES.indexOf(o1) - ORDERED_ENTRIES.indexOf(o2);
}
if (ORDERED_ENTRIES.contains(o1)) {
// o1 is in the ordered list, but o2 isn't. o1 is smaller (i.e. first)
return -1;
}
if (ORDERED_ENTRIES.contains(o2)) {
// o2 is in the ordered list, but o1 isn't. o2 is smaller (i.e. first)
return 1;
}
return o1.toString().compareTo(o2.toString());
}
}
Now, you can just sort:
public static void main(String[] args) {
List<StarTrek> cast = Arrays.asList(StarTrek.values());
Collections.sort(cast, new StarTrekSorter());
for (StarTrek trek : cast) {
System.out.println(trek);
}
}
which prints
Picard
Worf
William
Dianna
Geordi
Q
I would do it like this:
JeanLucPicard("Picard", 0),
GeordiLaForge("Geordi"),
DiannaTroi("Dianna"),
Worf("Worf", 1),
WilliamRiker("William", 2),
Q("Q");
StarTrek(String label) { this(label, -1); }
StarTrek(String label, int orderHint) { this.label=label; this.orderHint=orderHint; }
And in the compare method something like this:
if (orderHint == -1) {
return o1.label.compareTo(o2.label));
}
return o2.orderHint-o1.orderHint;
List the enums you want special ordering for first in your list, then use this code:
Comparator<StarTrek> comp = new Comparator<StarTrek>() {
public int compare(StarTrek o1, StarTrek o2) {
if (o1.ordinal() < 3)
return o2.ordinal() < 3 ? o1.ordinal() - o2.ordinal() : 1;
return o2.ordinal() < 3 ? -1 : o1.name().compareTo(o2.name());
}
};
You could define an additional constructor in your enum, that takes an index parameter, then provide indexes for the instances you want to go first (leave the ones you want to go in alphabetical order unindexed):
enum StarTrek {
JeanLucPicard("Picard"),
GeordiLaForge("Geordi"),
DiannaTroi("Dianna"),
Worf("Worf", 2),
WilliamRiker("William", 1),
Q("Q");
private final String label;
private final Integer index;
StarTrek(final String label, final Integer index ) { this.label = label; this.index = index; }
StarTrek(final String label) { this.label = label; this.index = Integer.MAX_VALUE; }
#Override public String toString() { return label; }
public Integer getIndex() {
return index;
}
}
Then your comparator would have to look like:
final Comparator<StarTrek> comp = new Comparator<StarTrek>() {
#Override
public int compare(final StarTrek o1, final StarTrek o2) {
if (!o1.getIndex().equals(o2.getIndex())) {
return o1.getIndex().compareTo(o2.getIndex());
}
return o1.toString().compareTo(o2.toString());
}
};
I tried looking up tutorials and videos and I understand what implementing does, although I'm a bit confused as to how one would implement a class from the Java Library. In my homework, I'm suppose to utilize the class, DataSet and make it so it accepts Comparable objects. The program is suppose to record the Min and Max values depending on the objects, in this case, I'm suppose to use strings. I wasn't sure if I needed any classes to implement the Comparable interface, so I made two classes just in case I was suppose to do so. My real question is how do I actually incorperate a String variable in the tester class to actually read and compare the object to another? thanks in advance.
public class Word implements Comparable
{
private String str;
public Word()
{
str = null;
}
public Word(String s)
{
str = s;
}
public int compareTo(Object other)
{
String n = (String) other;
return str.compareTo(n);
}
}
I wasn't sure which of the two classes would be suitable for implementing Although i think the String class below would not work at all b/c It's already a standard class so I wasn't too sure about using it
public class String implements Comparable
{
public String s;
public String()
{
s = null;
}
public String(String str)
{
s = str;
}
public int compareTo(Object other)
{
String n = (String) other;
return s.compareTo(n);
}
}
public interface Comparable
{
public int compareTo(Object other);
}
public class DataSet
{
private Object maximum;
private Object least;
private Comparable compare;
private int count;
public DataSet(Comparable s)
{
compare = s;
}
public void add(Object x)
{
if(count == 0)
least = x;
if(count == 0 || compare.compareTo(x) >=0)
maximum = x;
else if(compare.compareTo(x) <0)
least = x;
count++;
}
public Object getMaximum()
{
return maximum;
}
public Object getLeast()
{
return least;
}
}
public class DataSetTester
{
public static void main(String[] args)
{
Comparable n = new Word("sand");
DataSet data = new DataSet(n);
data.add(new Word(man));
System.out.println("Maximum Word: " + data.getMaximum());
System.out.println("Least Word: " + data.getLeast());
}
}
An interface is a contract that showes that your class contain all methodes that are implemented in the interface. In this case the CompareTo(object other). The String class already implements the comparable interface so you don't need youre own class. I think your data set class should look something like this :
public class DataSet<T implements Comparable>
{
private T maximum;
private T least;
private T count;
public void add(T x)
{
if(count == 0){
least = x;
maximum = x;
}
else if(least.compareTo(x) > 0)
least = x;
else if(maximum.compareTo(x) < 0)
maximum = x;
count++;
}
public T getMaximum()
{
return maximum;
}
public T getLeast()
{
return least;
}
}
T is a generic type and in your case it should be String, Here is how you create a new Data set:
DataSet<String> ds = new DataSet<String>;
Please review code:
/* Run1.java */
package test;
import java.util.Iterator;
import java.util.SortedSet;
import java.util.TreeSet;
public class Run1
{
static public void main(String[] args)
{
SortedSet<TestClass> s = new TreeSet<TestClass>();
s.add( new TestClass("name1", 100) );
s.add( new TestClass("name2", 10) );
s.add( new TestClass("name3", 1) );
s.add( new TestClass("name4", 10) );
s.add( new TestClass("name5", 100) );
Iterator<TestClass> it = s.iterator();
while(it.hasNext())
{
TestClass t = it.next();
System.out.println( t.name+' '+t.value );
}
}
}
/* TestClass.java */
package test;
public class TestClass implements Comparable<TestClass>
{
public String name;
public int value;
public TestClass(String name, int value) {
this.name = name;
this.value = value;
}
public int compareTo(TestClass o)
{
return this.value - o.value;
}
public boolean equals(Object o)
{
if (!(o instanceof TestClass))
return false;
TestClass n = (TestClass)o;
return this.name.equals(n.name);
}
public int hashCode()
{
return 31*name.hashCode();
}
public String toString()
{
return name;
}
}
Print out
name3 1
name2 10
name1 100
as i see because compareTo used for checking to equality (when returned 0). But i need check for unique by field TestClass.name and only sort by TestClass.value
The result of compareTo() and equals() need to be compatible in this case, which means that you need to take into account equality in the comparison. For example:
public int compareTo(TestClass o)
{
return (this.value == o.value) ? this.name.compareTo(o.name) : this.value - o.value;
}
which introduces a sub-order on name for objects with the same value, making the result compatible with your equals() implementation.
how about hacking the compareTo method as follows:
public int compareTo(TestClass o)
{
if (this.name != null && this.name.equals(o.name)) {
return 0;
}
return this.value - o.value;
}
This should do equality checks on name (remove duplicates) while sorting on value
If I understand correctly, then what you want is for your compareTo to always implement the "natural order" for the class. This means the way the client of the class would expect the class the behave. Contractually, compareTo should be consistent with equals, which is why I always implement equals as:
return compareTo(obj)==0;
This guarantees consistency.
Then, if you want another sort order, you should implement another class that implements Comparable. In this way you can have class consistency and separate sort orders.
Write comparator which compares TestClass objects.
public class TVComparator implements Comparator<TestClass> {
public int compare(TestClass o1, TestClass o2) {
if (o1.name.equals(o2.name)) return 0;
return o1.value - o2.value;
}
}
For sake of simplicity I have omitted any checks for null values.
I'm curious. What could be the reason that a Comparator shuffles entries on each
application start?
final static class ContactsListComparator implements Comparator
{
public int compare(Object o1, Object o2)
{
if((o1.toString().compareTo(o2.toString()))<0)
{
return -1;
}
if((o1.toString().compareTo(o2.toString()))>0)
{
return 1;
}
else
{
return 0;
}
}
}
First App Start:
Second App Start
As mentioned in one the answer
The Comparator actually compares an custom object Contact
public class Contact
{
// Members
private String _contactFirstName;
private String _contactLastName;
private long _contactLastModified;
// Constructor
public Contact()
{
set_contactLastModified();
}
public Contact(String contactFirstName)
{
_contactFirstName = contactFirstName;
set_contactLastModified();
}
// Accessable Getters
public String get_contactFirstName()
{
return _contactFirstName;
}
public String get_contactLastName()
{
return _contactLastName;
}
public long get_contactLastModified()
{
return _contactLastModified;
}
public void set_contactLastModified()
{
_contactLastModified = System.currentTimeMillis();
}
}
your toString method probably isn't overridden for your objects representing the contacts. It will return a hash string for those objects, which varies every time your app is run.
You can fix this either of two ways:
Override the toString() method in your Contact object to return the contact's name (1), or
Change the Comparator to Comparator<Contact> so it gets Contact objects as parameters (2)
for (1), add this to your Contact class:
#Override public String toString() {
return get_contactFirstName();
}
for (2) you would end up with this Comparator implementation:
final static class ContactsListComparator implements Comparator<Contact> {
public int compare(Contact o1, Contact o2) {
return contact1.get_contactFirstName().compareTo(contact2.get_contactFirstName());
}
}
you don't even need to check for the <0 or >0, but you can just return whatever the String comparison gives.
I would use:
final static class ContactsListComparator implements Comparator<Contact>
{
public int compare(Contact c1,Contact c2)
{
int i=c1.get_contactLastName().compareTo(c2.get_contactLastName());
if(i!=0) return i;
return c1.get_contactFirstName().compareTo(c2.get_contactFirstName());;
}
}
Your first example is basically the same as
final static class ContactsListComparator implements Comparator {
public int compare(Object o1, Object o2) {
return o1.toString().compareTo(o2.toString());
}
}
This would work if you override toString() like
public String toString() {
return _contactFirstName + ' ' + _contactLastName;
}
However, a comparator which compares the intended fields is better as has been suggested.