Using the Comparable interface when comparing Strings - java

I searched for this question, but I only found one thread that was kind of confusing, so I'm going to ask here for what I hope will be a clearer answer.
I have an assignment to use the Comparable interface to sort objects in an array by customer name. I have only done this with integers so far, so I'm not sure how to compare the strings together. How would I go about that? Here is where I am so far, assuming I am to use a.name compared to this.name:
public int comparedTo(Customer a)
{
} //end comparedTo
I also need to make a class to implement the Comparator interface to sort the values based on customer purchases and I think I did that properly, but I'd like to make sure before I go ripping my hair out when it's wrong. Here is what I did for that:
class NameComparator implements Comparator{
public int compare(Object cust1, Object cust2){
String cust1Purch = ((Customer)cust1).purchase;
String cust2Purch = ((Customer)cust2).purchase;
return cust1Purch.compareTo(cust2Purch);
}
Any help is greatly appreciated!

Its all ok, but you can specify Comparator generic type and then no need to cast objects:
class NameComparator implements Comparator<Customer>{
public int compare(Customer cust1, Customer cust2){
String cust1Purch = cust1.purchase;
String cust2Purch = cust2.purchase;
return cust1Purch.compareTo(cust2Purch);
}

Here is a complete example that might help you:
A CustomerComparator:
class CustomerComparator implements Comparator<Customer> {
#Override
public int compare(Customer c1, Customer c2) {
return c1.name.compareTo(c2.name); // or, simply c1.compareTo(c2);
}
}
A Comparable Customer:
class Customer implements Comparable<Customer> {
String name;
public Customer(String name) {
this.name = name;
}
#Override
public int compareTo(Customer o) {
return name.compareTo(o.name);
}
public String toString() {
return name;
}
}
A simple test driver:
public class Test {
public static void main(String[] args) {
List<Customer> customers = Arrays.asList(new Customer("Bravo"),
new Customer("Charlie"),
new Customer("Delta"),
new Customer("Alpha"));
Collections.sort(customers);
// Or
// Collections.sort(customers, new CustomerComparator());
System.out.println(customers);
}
}
(ideone.com demo)

Looks fine. But you can utilize Generics:
class NameComparator implements Comparator<Customer> {
public int compare(Customer cust1, Customer cust2) {..}
}

I seem to get it right for the Comparable interface. Nothing really complicated there.
As for the Comparator, if you're not using generics, you also need to validate both argument for the same base type, at least Comparable since you're using that interface :
if (cust1 instanceof Comparable && cust2 instanceof Comparable) {
Comparable c1 = (Comparable) cust1;
Comparable c2 = (Comparable) cust2;
return c1.compareTo(c2);
} else {
return false;
}

1) I would use generics to define your comparator and avoid additinal class casting:
class NameComparator implements Comparator<Customer> {
public int compare(Customer cust1, Customer cust2) {
...
}
}
2) String class in java already implements Comparable interface ( http://download.oracle.com/javase/1.5.0/docs/api/java/lang/String.html ). So, if you need to just compare on customer's name or purchase string, then you can just delegate it to String and that's what you already do.

Related

Collection sort that sort based on class that implements Comparator

I have a problem with Collections sorting, to be more specific
Output:
[B_Author: Movie_X, A_Author: Movie_A, A_Author: Movie_B]
Should Be:
[A_Author: Movie_A, A_Author: Movie_B, B_Author: Movie_X]
method thats supposed to do that (in class MovieStorage):
public Set<Movie> getCatalogue(Comparator<Movie> comp){
List<Movie> sett = new ArrayList<>(this.movieList);
sett.sort(comp);
return new HashSet<>(sett);
}
Class that implements comparator:
public class MovieComparator implements Comparator<Movie>
{
#Override
public int compare(Movie a, Movie b) {
if (a.getName().compareTo(b.getName()) == 0){
return a.getAuthor().compareTo(b.getAuthor());
}
return a.getName().compareTo(b.getName());
}
}
called method:
System.out.println(movieStorage.getCatalogue(new MovieComparator()));
I have been looking around StackOverflow and other webs, but I have noticed that everyone sorts it based on 1 parameter ( which doesnt work for me too ) and at a same time, the code is pretty much the same as mine is ...
Thanks for help in advance :)
A more compact way to create comparators:
Comparator<Movie> comparator =
Comparator.comparing(Movie::getName).thenComparing(Movie::getAuthor);
Set<Movie> movies = new TreeSet<>(comparator);
movies.addAll(movieList);
And TreeSet iterates in sorted order.
To add to the Movie class:
public static class Movie implements Comparable<Movie> {
private static final Comparator<Movie> COMPARATOR =
Comparator.comparing(Movie::getName).thenComparing(Movie::getAuthor);
...
#Override
public int compareTo(Movie other) {
return COMPARATOR.compare(this, other);
}
}
Then the TreeSet doesn't need a comparator specified:
Set<Movie> movies = new TreeSet<>(movieList);

impose order (not sorting) using comparator on a wrapper class of a list in Java

I have a class, person. it has Age, Name, Height, etc.
I am creating a class called PersonCollection which is a wrapper of a list (an ArrayList).
I will like to be able to compare Person objects using the PersonCollection class, Which means, I don't want to make the Person class implement the Comparable interface, I would like the PersonCollection to implement the Comparator interface.
I have having trouble doing that. I have implemented the compare method but still when I compare Person Objects it doesn't work.
for example this code gives me an error (people is an ArrayList
public void insert (Person p){
for(int i = 0; i < people.size(); i++){
if (people.get(i) > p){
//Do something
}
}
}
I know how to use Comparator for sorting, this is different.
I am fully aware of other possible and maybe better solutions (any priority queue class or some sort of sortedset classes)
I wish to do that for ArrayList for a specific reason and I kindly ask you to base your solutions on this instead of suggest other Data structures.
You can write a custom Comparator and use the compare(a, b) method.
https://docs.oracle.com/javase/8/docs/api/java/util/Comparator.html#compare-T-T-
Your code would look like
if (myComparator.compare(people.get(i), p) > 0 ) {
According to your description you have a Wrapper class like this?
public class People implements List<Person>, Comparator<Person>{
/* methods */
}
so if you want to really use comparator interface, then you would have to do it like this:
public void insert (Person p){
for(int i = 0; i < people.size(); i++){
if (people.compare(people.get(i),p)){ // because people implements Comparator
//Do something
}
}
}
which should (not too sure though) work.
But i would highly recommend not to use this and think about something better, as a class should not be a comparator and a list (because both interfaces should be used for completly different reasons).
A better approach would be to make Person implement Comparable, and then sort according to that
Below is a piece of code where you can see a custom comparator is making an age comparison on Person object's age attribute.
public class TestCompare {
public static void main(String[] args) {
Person person1 = new Person(45, "Tom");
Person person2 = new Person(12, "Sarah");
Person person3 = new Person(34, "Michael");
Person person4 = new Person(33, "Donald");
Person person5 = new Person(65, "timothy");
List<Person> people = new ArrayList<Person>();
people.add(person1);
people.add(person2);
people.add(person3);
people.add(person4);
people.add(person5);
CustomComparator comparator=new CustomComparator();
for (Person p : people) {
System.out.println(comparator.compare(p, new Person(55, "James")));
}
}
}
class CustomComparator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
// TODO Auto-generated method stub
return o1.getAge().compareTo(o2.getAge());
}
}
class Person implements Comparable<Person> {
public Person(int age, String name) {
this.age = age;
this.name = name;
}
private Integer age;
private String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public int compareTo(Person o) {
// TODO Auto-generated method stub
return this.getAge().compareTo(o.getAge());
}
}

How to find Set difference with custom Comparator or equals method?

I want to find the difference between two Set<T> using a different equality metric than that used by the class T, for example a custom Comparator<T>
For example, I have a class Animal, which usually tests equality using the species of the Animal
public class Animal {
public String species;
public String genus;
public Animal(String species, String genus){
this.species = species;
this.genus = genus;
}
public boolean equals(Animal other){
return other.species.equals(this.species);
}
}
I have two List<Animal> and I'd like to find the intersection of shared genus between the two lists.
Usually, I'd convert the List to Set and use retainAll to find the intersection. But here, that would give the intersection of shared species, not shared genus.
I'd like to use something like the GenusComparator to define equality for the intersection.
public class GenusComparator implements Comparator<Animal>{
#Override
public int compare(Animal animal1, Animal animal2) {
return String.CASE_INSENSITIVE_ORDER.compare(animal1.genus, animal2.genus);
}
}
This is just a simple example to explain what I'm trying to do, not the actual classes in my application.
Two possible solutions that I have found are
Wrap the class and override the equals method
Use a TreeSet with a custom Comparator
Are there other ways I've missed so far, and what are the possible pros and cons of these solutions?
The easiest way to do it, is to simple use a TreeSet with the GenusComparator.
You have to convert both sets to TreeSet(GenusComparator) for the retainAll() to work correctly.
I fixed equals() and added hashCode() and toString().
public class Test {
public static void main(String[] args) {
Set<Animal> set1 = new HashSet<>(Arrays.asList(new Animal("Jaguar", "Panthera"),
new Animal("Margay", "Leopardus"),
new Animal("Tiger", "Panthera")));
Set<Animal> set2 = new HashSet<>(Arrays.asList(new Animal("Bobcat", "Lynx"),
new Animal("Cougar", "Puma"),
new Animal("Leopard", "Panthera")));
TreeSet<Animal> treeSet1 = new TreeSet<>(new GenusComparator());
treeSet1.addAll(set1);
TreeSet<Animal> treeSet2 = new TreeSet<>(new GenusComparator());
treeSet2.addAll(set2);
treeSet1.retainAll(treeSet2);
System.out.println(treeSet1);
}
}
class Animal {
public String species;
public String genus;
public Animal(String species, String genus) {
this.species = species;
this.genus = genus;
}
#Override
public boolean equals(Object obj) {
return obj instanceof Animal && this.species.equals(((Animal)obj).species);
}
#Override
public int hashCode() {
return this.species.hashCode();
}
#Override
public String toString() {
return this.species + "/" + this.genus;
}
}
class GenusComparator implements Comparator<Animal> {
#Override
public int compare(Animal animal1, Animal animal2) {
return String.CASE_INSENSITIVE_ORDER.compare(animal1.genus, animal2.genus);
}
}
Output
[Jaguar/Panthera]

Sort arraylist in specific order [duplicate]

This question already has answers here:
Sort ArrayList of custom Objects by property
(29 answers)
Closed 7 years ago.
I have an arraylist like below:
ArrayList<PhoneNumber> arrayListToSort
PhoneNumber have following properties
private String number;
private String numberType;
Number type is like MOBILE , HOME , OFFICE
I want to sort arrayListToSort by numberType of each.
I can do this with simple alphebetical order.
But my problem is how to sort the list by follwing order.
MOBILE
HOME
OFFICE
Hey you can try PhonNumberType with enum check below code
package test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
enum PhonNumberType {
MOBILE, HOME, OFFICE
}
class PhoneNumber implements Comparable<PhoneNumber> {
private String number;
private PhonNumberType phonNumberType;
#Override
public int compareTo(PhoneNumber o) {
return this.phonNumberType.compareTo(o.phonNumberType);
}
public String getNumber() {
return number;
}
public void setNumber(String number) {
this.number = number;
}
public PhonNumberType getNumberType() {
return phonNumberType;
}
public void setNumberType(PhonNumberType phonNumberType) {
this.phonNumberType = phonNumberType;
}
#Override
public String toString() {
return "PhoneNumber [number=" + number + ", phonNumberType=" + phonNumberType + "]";
}
}
public class T {
public static void main(String[] args) {
List<PhoneNumber> test = new ArrayList<PhoneNumber>();
PhoneNumber pn = new PhoneNumber();
pn.setNumber("1");
pn.setNumberType(PhonNumberType.HOME);
test.add(pn);
pn = new PhoneNumber();
pn.setNumber("2");
pn.setNumberType(PhonNumberType.MOBILE);
test.add(pn);
pn = new PhoneNumber();
pn.setNumber("3");
pn.setNumberType(PhonNumberType.OFFICE);
test.add(pn);
Collections.sort(test);
System.out.println(test);
}
}
output is : [PhoneNumber [number=2, phonNumberType=MOBILE],
PhoneNumber [number=1, phonNumberType=HOME], PhoneNumber [number=3,
phonNumberType=OFFICE]]
Your Ranking is no natural order.
To get your goal result, you must define your own Comparator.
By the way, as #Alexis C. says, use an Enum for your numberType
Use an enum for that:
public enum NumberType {
MOBILE,HOME,OFFICE;
}
If your PhoneNumber is comparable:
#Override
public int compareTo(PhoneNumber o) {
return numberType.compareTo(o.numberType);
}
It will follow the order described in the enum:
MOBILE,HOME,OFFICE
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
public class Main {
public static void arrayListToSort(ArrayList<PhoneNumber> arrayListToSort) {
Map<String, Integer> phoneTypeToOrder = new HashMap<>();
phoneTypeToOrder.put("MOBILE", 0);
phoneTypeToOrder.put("HOME", 1);
phoneTypeToOrder.put("OFFICE", 2);
Collections.sort(arrayListToSort, new Comparator<PhoneNumber>() {
#Override
public int compare(PhoneNumber o1, PhoneNumber o2) {
if (o1 == null || o1.numberType == null) {
return -1;
}
if (o2 == null || o2.numberType == null) {
return 1;
}
Integer o1Order = phoneTypeToOrder.get(o1.numberType);
Integer o2Order = phoneTypeToOrder.get(o2.numberType);
return o1Order.compareTo(o2Order);
}
});
}
}
class PhoneNumber {
String number;
String numberType;
}
I suggest using a customing "sorting-method" called Comparator. In this Comparator you define a "value" for each your records which will be calculated at runtime. The calculation of this value is entirely up to you. You can for example define that "foo" == 1, "bar" == 2 and "john" == 654. The sorting method of Java will then ask each record for it's "value" and sort accordingly (I think using Quicksort-Algorithm, but I am not sure about this).
Here's a nice little tutorial to get you into this topic and shows how to implement Comparator and Comparable-Interfaces correctly:
http://www.tutorialspoint.com/java/java_using_comparator.htm
This one is a bit more indepth: http://www.journaldev.com/780/java-comparable-and-comparator-example-to-sort-objects
Besides: as stated before, use enums for your number-types. not because it's easier (imho) but because it is much faster comparing enums than comparing Strings.
You can use a TreeSet collection, which sorts the entries.
TreeSet<PhoneNumber> sortedList = new TreeSet<>((PhoneNumber p1, PhoneNumber p2) -> p1.compareTo(p2));
Since you have to define an order for the entries, your PhoneNumber class will have to implement the Comparable interface and you need to override the compareTo() method to say that you will compare on the numberType
public int compareTo(Object o) {
PhoneNumber p = (PhoneNumber) o;
return numberType.equals(o.getNumberType())?0:numberType.equals("MOBILE")?-1:1;
}
However, better to use an enum if you want to add more number types in future
If you make your NumberType enum, not String, its compareTo() method will return result based on order in which enum instances are declared, not alphabetical:
public enum NumberType {
MOBILE, HOME, OFFICE;
}
MOBILE now is less than HOME and HOME is less than OFFICE. Now you may sort your collection using custom comparator:
list.sort(comparing(PhoneNumber::getNumberType).thenComparing(PhoneNumber::getNumber));
There are two ways to do this.
Use a Comparator
In this approach, you sort the ArrayList using a custom Comparator.
Here is the Comparator code. Notice how it first tries to sort on numberType, and then number:
public class PhoneNumberComparator implements Comparator<PhoneNumber> {
#Override
public int compareTo (PhoneNumber lhs, PhoneNumber rhs) {
int result = lhs.numberType.compareTo(rhs.numberType);
if (result == 0) {
result = lhs.number.compareTo(rhs.number);
}
return result;
}
}
Then you can call:
Comparator c = new PhoneNumberComparator();
arrayListToSort.sort (c)
Now by itself, this won't work completely because the sort of numberType will just be in string order. The most convenient way to impose the ordering is to make numberType an enumeration. Here is the code for that:
public enum NumberType { MOBILE, HOME, OFFICE }
Then the PhoneNumber must be defined so that numberType is a NumberType:
public class PhoneNumber {
public String number ;
public NumberType numberType;
// .......
}
(By the way I would encourage you to also make number and numberType into private variables, and add getter methods, as per the JavaBeans standard.)
Make PhoneNumber Implement Comparable
If you are planning to do this sort often, then instead of writing the Comparator, you should make PhoneNumber implement the Comparable interface:
public class PhoneNumber implements Comparable <PhoneNumber> {
public String number ;
public NumberType numberType;
// .......
#Override
public int compareTo (PhoneNumber rhs) {
int result = this.numberType.compareTo(rhs.numberType);
if (result == 0) {
result = this.number.compareTo(rhs.number);
}
return result;
}
}
Then you can just call:
arrayList.sort()
You still need to make NumberType an enum, as discussed in the first approach. The above compareTo() implementation relies on NumberType being Comparable; all enum instances are automatically Comparable.
Notice the similarities and the differences between the Comparator and the compareTo() implementations. A good discussion of Comparator and Comparable can be found here.

How to sort a list of structs by an element of the struct in java

I have a list of structs that I would like to sort according to a specific element of the struct:
private class myStruct {
public Boolean GUI;
public float CallTime;
public String ReqID;
public String ReqGUID;
public String Stereotype;
public String StereotypeGUID;
}
private List<myStruct> DataList = new ArrayList<myStruct>();
How could I sort DataList by the element "ReqID" without hardcoding it?
Is there a possibility to use Arrays.sort()?
You should use a Comparator.
class YourComparator implements Comparator<myStruct>{
public int compare(myStruct s1, myStruct s2){
//here comes the comparison logic
}
}
And then use this form of the sort() method:
Arrays.sort(T[] arrayToSort, Comparator<T> yourComparator);
It's not very clear whether you use a collection or an array as the data structure.
In case you use a List, then use Collections.sort().
For custom sorting you can implement the Comparable interface.
With this interface you create a method compareTo() which returns a negative number, 0 or a positive number. Based on the return code Collections.sort() can tell if the element has to be before or after another element.
A nice example how to use it can be found in this answer: java class implements comparable
use the Comparator interface like this
public static void main(String[] args) {
List<myStruct> DataList = new ArrayList<myStruct>();
//ADD Objects to DataList here
Collections.sort(DataList, new Comparator() {
public int compare(Object o1, Object o2) {
myStruct p1 = (myStruct) o1;
myStruct p2 = (myStruct) o2;
int ret = -1;
//business logic here
if (Integer.parseInt(p1.ReqGUID) == Integer.parseInt(p2.ReqGUID)) {
ret = 0;
} else if (Integer.parseInt(p1.ReqGUID) > Integer.parseInt(p2.ReqGUID)) {
ret = 1;
} else if (Integer.parseInt(p1.ReqGUID) < Integer.parseInt(p2.ReqGUID)) {
ret = -1;
}//end business logic
return ret;
}
});
}
Here inside the Collections.sort() method I am implementing the Comparator interface and overriding the compare() method. This will actually sort your list based on the business logic you implemented inside the compare() method;
Your class structure look strange to me.. You have public fields inside a private class..
Ideally your fields should be marked private and you can have getters and setters to access them..
As for your problem, you can take a look at two important interfaces taht are defined for this kind of job: - http://docs.oracle.com/javase/1.4.2/docs/api/java/util/Comparator.html and http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Comparable.html..
You use Comparator when you want to have multiple ways to compare your class instance..
You can just create a class implementing the Comparator interface, and pass the instance of this class to Collections.sort() method to use this Comparator for sorting..
In this case, compare() method is used to do the job of comparison.
Alternatively, you can associate only one way of comparing a class instance by making that class implementing Comparable interface.. In this case you need to override compareTo() method..
Here's a sample code using Comparator: -
public class MyComparator implements Comparator<Box> {
#Override
public int compare(Box box0, Box box1) {
int w0 = box0.getWeight();
int w1 = box1.getWeight();
return (w0 > w1? -1 : (w0 == w1) ? 0 : 1);
}
}
public class Box {
private int weight;
public Box() {
}
public Box(int weight) {
this.weight = weight;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
And Your Main
public class Main {
public static void main(String args[]) {
List<Box> boxList = new ArrayList<Box>();
Collections.sort(boxList, new MyComparator());
}
}
Hope it helps..
You can define your own Comparator and use Collections.sort(), passing that Comparator in. That way you can define different comparators for searching using different fields.
Alternatively your struct can implement the Comparable interface and Collections.sort() can sort using this. This is called using the natural sort order, since it's implicit to your class.
Here's the Java tutorial on sorting and ordering.
Use java.util.Collections.sort() with a Comparator instance. See the JavaDocs.
Use Arrays.sort(T[] a, Comparator<? super T> c)
or Collections.sort(List a, Comparator c)
Simplest solution
Just implement java.lang.Comparable interface in you class like following:
class MyStruct implements Comparable<MyStruct>{
public Boolean GUI;
public float CallTime;
public String ReqID;
public String ReqGUID;
public String Stereotype;
public String StereotypeGUID;
#Override
public int compareTo(MyStruct other) {
return ReqID.compareTo(other.ReqID);
/* also you can use ReqID.compareToIgnoreCase(other.ReqID); */
}
#Override
public String toString() {
return "(" + ReqID + ")";
}
}
Override also toString() method just for printing.
Also, keep in mind that String's compareTo() method sorts using lexicographical order. In case you want numeric ID's it is better to use int or other numerical type.
Following is complete code to sort using Arrays.sort() as well as Collections.sort() - choose what suits you :)
public class MyStructSort {
private final static String[] STRUCT_IDS = {"C", "D", "A", "Aa", "B", "Z", "Aaa" };
private static List<MyStruct> createList() {
List<MyStruct> structList = new ArrayList<MyStruct>();
for (String id: STRUCT_IDS) {
MyStruct struct = new MyStruct();
struct.ReqID = id;
structList.add(struct);
}
return structList;
}
public static void main(String[] args) {
List<MyStruct> dataList = createList();
/* Sort using Lists (Collections) */
Collections.sort(dataList);
System.out.println("List sort:\t" + dataList);
/* Sort using arrays */
MyStruct[] dataArray = dataList.toArray(new MyStruct[dataList.size()]);
Arrays.sort(dataArray);
// print sorted array
System.out.print("Array sort:\t");
for (MyStruct struct: dataArray) {
System.out.print(struct+" ");
}
}
}
This is just demonstration code, so some null-checks and getters and setters would be needed to make it perfect.

Categories

Resources