Uncompilable source code when using Comparator in Java - java

I'm getting the following error when trying to run this little program:
Exception in thread "main" java.lang.ExceptionInInitializerError
at MainClass.main(Person.java:120)
Caused by: java.lang.RuntimeException: Uncompilable source code - FirstNameComparator is not abstract and does not override abstract method compare(java.lang.Object,java.lang.Object) in java.util.Comparator
at FirstNameComparator.<clinit>(Person.java:71)
... 1 more
Java Result: 1
Please tell me what I'm doing wrong, and of course it would help a short example with explanations about how to properly use Comparator and Comparable.
import java.util.*;
import java.lang.*;
public class Person implements Comparable {
private String firstName;
private String lastName;
private int age;
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public void setLastName(String lastName) {
this.lastName= lastName;
}
public String getLastName() {
return lastName;
}
public void setAge(int age) {
this.age=age;
}
public int getAge() {
return age;
}
#Override
public int compareTo(Object anotherPerson) throws ClassCastException {
if(!(anotherPerson instanceof Person)) {
throw new ClassCastException("A person object expected !");
}
int anotherPersonAge = ((Person)anotherPerson).getAge();
return this.age = anotherPersonAge;
}
}
LastNameComparator.java
class LastNameComparator implements Comparator {
public int compareTo(Object person, Object AnotherPerson) {
String lastName1 = ((Person)person).getLastName().toUpperCase();
String firstName1= ((Person)person).getFirstName().toUpperCase();
String lastName2 = ((Person)person).getLastName().toUpperCase();
String firstName2 = ((Person)person).getFirstName().toUpperCase();
if(lastName1.equals(lastName2)) {
return firstName1.compareTo(firstName2);
}else {
return lastName1.compareTo(lastName2);
}
}
}
FirstNameComparator.java
class FirstNameComparator implements Comparator {
public int compareTo(Object person, Object anotherPerson) {
String lastName1 = ((Person)person).getLastName().toUpperCase();
String firstName1 = ((Person)person).getFirstName().toUpperCase();
String lastName2 = ((Person) person).getLastName().toUpperCase();
String firstName2 = ((Person) person).getFirstName().toUpperCase();
if(firstName1.equals(lastName1)) {
return lastName1.compareTo(lastName2);
}else {
return firstName1.compareTo(firstName2);
}
}
}
MainClass.java
class MainClass {
public static void main(String[] args) {
Person[] persons = new Person[4];
persons[0] = new Person();
persons[0].setFirstName("Asaftei");
persons[0].setLastName("Ionut");
persons[0].setAge(25);
persons[1] = new Person();
persons[1].setFirstName("Binoclu");
persons[1].setLastName("Telescop");
persons[1].setAge(10);
persons[2] = new Person();
persons[2].setFirstName("Morcov");
persons[2].setLastName("Castravete");
persons[2].setAge(33);
persons[3] = new Person();
persons[3].setFirstName("Rosie");
persons[3].setLastName("Ardei");
persons[3].setAge(55);
System.out.println("Natural Order : ");
for(int counter =0; counter<=3; counter++) {
Person person = persons[counter];
String lastName=person.getLastName();
String firstName=person.getFirstName();
int age = person.getAge();
System.out.println(lastName+ ", " +firstName+ " , "+age);
}
Arrays.sort(persons, new FirstNameComparator());
System.out.println();
System.out.println("Sorted by First Name: ");
for(int counter=0; counter<=3; counter++) {
Person person = persons[counter];
String lastName = person.getLastName();
String firstName = person.getFirstName();
int age = person.getAge();
System.out.println(lastName+ ", " +firstName+ ", " +age );
}
Arrays.sort(persons, new LastNameComparator());
System.out.println();
System.out.println("Sorted by Last Name"); for(int counter=0; counter<=3; counter++) {
Person person = persons[counter];
String lastName = person.getLastName().toUpperCase();
String firstName = person.getFirstName().toUpperCase();
int age = person.getAge();
System.out.println(lastName +", "+firstName+", "+age);
}
}
}

Comparators need to implement the compare method, not a compareTo method.
So, e.g., your FirstNameComparator should look like this:
class FirstNameComparator implements Comparator {
#Override
public int compare(Object person, Object anotherPerson) {
// implementation
}
}
Note that since this Comparator is used to compare Person instances, it would be a better practice to use generics syntax and define it as such:
class FirstNameComparator implements Comparator<Person> {
#Override
public int compare(Person person, Person anotherPerson) {
// implementation
}
}

Don't forget Comparable takes in generic type arguments.
So use implements Comparable<Person> instead. This means that you can compare two Persons.
Also an overriding method is not allowed to add additional throws to the method. See this question
The new definition should be something like this:
#Override
public int compareTo(Person other) {
return Intger.compare(this.getAge(), other.getAge());
}

Related

OrderBy under two criteria [duplicate]

I have array of objects person (int age; String name;).
How can I sort this array alphabetically by name and then by age?
Which algorithm would you use for this ?
You can use Collections.sort as follows:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons> is now sorted by name, then by age.
String.compareTo "Compares two strings lexicographically" - from the docs.
Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.
For those able to use the Java 8 streaming API, there is a neater approach that is well documented here:
Lambdas and sorting
I was looking for the equivalent of the C# LINQ:
.ThenBy(...)
I found the mechanism in Java 8 on the Comparator:
.thenComparing(...)
So here is the snippet that demonstrates the algorithm.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Check out the link above for a neater way and an explanation about how Java's type inference makes it a bit more clunky to define compared to LINQ.
Here is the full unit test for reference:
#Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* #param name The name of the person.
* #param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
#Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Using the Java 8 Streams approach, with method references on the getters...
// Create a stream...
var sortedList = persons.stream()
// sort it (does not sort the original list)...
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// and collect to a new list
.collect(Collectors.toList());
Collection to an array ist also possible:
persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
.toArray(String[]::new);
And the Java 8 Lambda approach...
//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
if (p1.getName().compareTo(p2.getName()) == 0) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
});
Lastly...
// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
You need to implement your own Comparator, and then use it: for example
Arrays.sort(persons, new PersonComparator());
Your Comparator could look a bit like this:
public class PersonComparator implements Comparator<? extends Person> {
public int compare(Person p1, Person p2) {
int nameCompare = p1.name.compareToIgnoreCase(p2.name);
if (nameCompare != 0) {
return nameCompare;
} else {
return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
}
}
}
The comparator first compares the names, if they are not equals it returns the result from comparing them, else it returns the compare result when comparing the ages of both persons.
This code is only a draft: because the class is immutable you could think of building an singleton of it, instead creating a new instance for each sorting.
Have your person class implement Comparable<Person> and then implement the compareTo method, for instance:
public int compareTo(Person o) {
int result = name.compareToIgnoreCase(o.name);
if(result==0) {
return Integer.valueOf(age).compareTo(o.age);
}
else {
return result;
}
}
That will sort first by name (case insensitively) and then by age. You can then run Arrays.sort() or Collections.sort() on the collection or array of Person objects.
Guava's ComparisonChain provides a clean way of doing it. Refer to this link.
A utility for performing a chained comparison statement. For example:
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}
You can do like this:
List<User> users = Lists.newArrayList(
new User("Pedro", 12),
new User("Maria", 10),
new User("Rafael",12)
);
users.sort(
Comparator.comparing(User::getName).thenComparing(User::getAge)
);
I would be careful when using Guava's ComparisonChain because it creates an instance of it per element been compared so you would be looking at a creation of N x Log N comparison chains just to compare if you are sorting, or N instances if you are iterating and checking for equality.
I would instead create a static Comparator using the newest Java 8 API if possible or Guava's Ordering API which allows you to do that, here is an example with Java 8:
import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;
private static final Comparator<Person> COMPARATOR = Comparator
.comparing(Person::getName, nullsLast(naturalOrder()))
.thenComparingInt(Person::getAge);
#Override
public int compareTo(#NotNull Person other) {
return COMPARATOR.compare(this, other);
}
Here is how to use the Guava's Ordering API: https://github.com/google/guava/wiki/OrderingExplained
Create as many comparators as necessary. After, call the method "thenComparing" for each order category. It's a way of doing by Streams. See:
//Sort by first and last name
System.out.println("\n2.Sort list of person objects by firstName then "
+ "by lastName then by age");
Comparator<Person> sortByFirstName
= (p, o) -> p.firstName.compareToIgnoreCase(o.firstName);
Comparator<Person> sortByLastName
= (p, o) -> p.lastName.compareToIgnoreCase(o.lastName);
Comparator<Person> sortByAge
= (p, o) -> Integer.compare(p.age,o.age);
//Sort by first Name then Sort by last name then sort by age
personList.stream().sorted(
sortByFirstName
.thenComparing(sortByLastName)
.thenComparing(sortByAge)
).forEach(person->
System.out.println(person));
Look: Sort user defined object on multiple fields – Comparator (lambda stream)
Use Comparator and then put objects into Collection, then Collections.sort();
class Person {
String fname;
String lname;
int age;
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public Person(String fname, String lname, int age) {
this.fname = fname;
this.lname = lname;
this.age = age;
}
#Override
public String toString() {
return fname + "," + lname + "," + age;
}
}
public class Main{
public static void main(String[] args) {
List<Person> persons = new java.util.ArrayList<Person>();
persons.add(new Person("abc3", "def3", 10));
persons.add(new Person("abc2", "def2", 32));
persons.add(new Person("abc1", "def1", 65));
persons.add(new Person("abc4", "def4", 10));
System.out.println(persons);
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person t, Person t1) {
return t.getAge() - t1.getAge();
}
});
System.out.println(persons);
}
}
Or you can exploit the fact that Collections.sort() (or Arrays.sort()) is stable (it doesn't reorder elements that are equal) and use a Comparator to sort by age first and then another one to sort by name.
In this specific case this isn't a very good idea but if you have to be able to change the sort order in runtime, it might be useful.
You can use generic serial Comparator to sort collections by multiple fields.
import org.apache.commons.lang3.reflect.FieldUtils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* #author MaheshRPM
*/
public class SerialComparator<T> implements Comparator<T> {
List<String> sortingFields;
public SerialComparator(List<String> sortingFields) {
this.sortingFields = sortingFields;
}
public SerialComparator(String... sortingFields) {
this.sortingFields = Arrays.asList(sortingFields);
}
#Override
public int compare(T o1, T o2) {
int result = 0;
try {
for (String sortingField : sortingFields) {
if (result == 0) {
Object value1 = FieldUtils.readField(o1, sortingField, true);
Object value2 = FieldUtils.readField(o2, sortingField, true);
if (value1 instanceof Comparable && value2 instanceof Comparable) {
Comparable comparable1 = (Comparable) value1;
Comparable comparable2 = (Comparable) value2;
result = comparable1.compareTo(comparable2);
} else {
throw new RuntimeException("Cannot compare non Comparable fields. " + value1.getClass()
.getName() + " must implement Comparable<" + value1.getClass().getName() + ">");
}
} else {
break;
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
}
Arrays.sort(persons, new PersonComparator());
import java.util.Comparator;
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
if(null == o1 || null == o2 || null == o1.getName() || null== o2.getName() ){
throw new NullPointerException();
}else{
int nameComparisonResult = o1.getName().compareTo(o2.getName());
if(0 == nameComparisonResult){
return o1.getAge()-o2.getAge();
}else{
return nameComparisonResult;
}
}
}
}
class Person{
int age; String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Updated version:
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName());
return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult;
}
}
For a class Book like this:
package books;
public class Book {
private Integer id;
private Integer number;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "book{" +
"id=" + id +
", number=" + number +
", name='" + name + '\'' + '\n' +
'}';
}
}
sorting main class with mock objects
package books;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
Book b = new Book();
Book c = new Book();
Book d = new Book();
Book e = new Book();
Book f = new Book();
Book g = new Book();
Book g1 = new Book();
Book g2 = new Book();
Book g3 = new Book();
Book g4 = new Book();
b.setId(1);
b.setNumber(12);
b.setName("gk");
c.setId(2);
c.setNumber(12);
c.setName("gk");
d.setId(2);
d.setNumber(13);
d.setName("maths");
e.setId(3);
e.setNumber(3);
e.setName("geometry");
f.setId(3);
f.setNumber(34);
b.setName("gk");
g.setId(3);
g.setNumber(11);
g.setName("gk");
g1.setId(3);
g1.setNumber(88);
g1.setName("gk");
g2.setId(3);
g2.setNumber(91);
g2.setName("gk");
g3.setId(3);
g3.setNumber(101);
g3.setName("gk");
g4.setId(3);
g4.setNumber(4);
g4.setName("gk");
List<Book> allBooks = new ArrayList<Book>();
allBooks.add(b);
allBooks.add(c);
allBooks.add(d);
allBooks.add(e);
allBooks.add(f);
allBooks.add(g);
allBooks.add(g1);
allBooks.add(g2);
allBooks.add(g3);
allBooks.add(g4);
System.out.println(allBooks.size());
Collections.sort(allBooks, new Comparator<Book>() {
#Override
public int compare(Book t, Book t1) {
int a = t.getId()- t1.getId();
if(a == 0){
int a1 = t.getNumber() - t1.getNumber();
return a1;
}
else
return a;
}
});
System.out.println(allBooks);
}
}
I'm not sure if it's ugly to write the compartor inside the Person class in this case. Did it like this:
public class Person implements Comparable <Person> {
private String lastName;
private String firstName;
private int age;
public Person(String firstName, String lastName, int BirthDay) {
this.firstName = firstName;
this.lastName = lastName;
this.age = BirthDay;
}
public int getAge() {
return age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public int compareTo(Person o) {
// default compareTo
}
#Override
public String toString() {
return firstName + " " + lastName + " " + age + "";
}
public static class firstNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.firstName.compareTo(o2.firstName);
}
}
public static class lastNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.lastName.compareTo(o2.lastName);
}
}
public static class ageComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.age - o2.age;
}
}
}
public class Test {
private static void print() {
ArrayList<Person> list = new ArrayList();
list.add(new Person("Diana", "Agron", 31));
list.add(new Person("Kay", "Panabaker", 27));
list.add(new Person("Lucy", "Hale", 28));
list.add(new Person("Ashley", "Benson", 28));
list.add(new Person("Megan", "Park", 31));
list.add(new Person("Lucas", "Till", 27));
list.add(new Person("Nicholas", "Hoult", 28));
list.add(new Person("Aly", "Michalka", 28));
list.add(new Person("Adam", "Brody", 38));
list.add(new Person("Chris", "Pine", 37));
Collections.sort(list, new Person.lastNameComperator());
Iterator<Person> it = list.iterator();
while(it.hasNext())
System.out.println(it.next().toString());
}
}

Comparator - Sorting a List of Objects without using Collections [duplicate]

I have array of objects person (int age; String name;).
How can I sort this array alphabetically by name and then by age?
Which algorithm would you use for this ?
You can use Collections.sort as follows:
private static void order(List<Person> persons) {
Collections.sort(persons, new Comparator() {
public int compare(Object o1, Object o2) {
String x1 = ((Person) o1).getName();
String x2 = ((Person) o2).getName();
int sComp = x1.compareTo(x2);
if (sComp != 0) {
return sComp;
}
Integer x1 = ((Person) o1).getAge();
Integer x2 = ((Person) o2).getAge();
return x1.compareTo(x2);
}});
}
List<Persons> is now sorted by name, then by age.
String.compareTo "Compares two strings lexicographically" - from the docs.
Collections.sort is a static method in the native Collections library. It does the actual sorting, you just need to provide a Comparator which defines how two elements in your list should be compared: this is achieved by providing your own implementation of the compare method.
For those able to use the Java 8 streaming API, there is a neater approach that is well documented here:
Lambdas and sorting
I was looking for the equivalent of the C# LINQ:
.ThenBy(...)
I found the mechanism in Java 8 on the Comparator:
.thenComparing(...)
So here is the snippet that demonstrates the algorithm.
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
Check out the link above for a neater way and an explanation about how Java's type inference makes it a bit more clunky to define compared to LINQ.
Here is the full unit test for reference:
#Test
public void testChainedSorting()
{
// Create the collection of people:
ArrayList<Person> people = new ArrayList<>();
people.add(new Person("Dan", 4));
people.add(new Person("Andi", 2));
people.add(new Person("Bob", 42));
people.add(new Person("Debby", 3));
people.add(new Person("Bob", 72));
people.add(new Person("Barry", 20));
people.add(new Person("Cathy", 40));
people.add(new Person("Bob", 40));
people.add(new Person("Barry", 50));
// Define chained comparators:
// Great article explaining this and how to make it even neater:
// http://blog.jooq.org/2014/01/31/java-8-friday-goodies-lambdas-and-sorting/
Comparator<Person> comparator = Comparator.comparing(person -> person.name);
comparator = comparator.thenComparing(Comparator.comparing(person -> person.age));
// Sort the stream:
Stream<Person> personStream = people.stream().sorted(comparator);
// Make sure that the output is as expected:
List<Person> sortedPeople = personStream.collect(Collectors.toList());
Assert.assertEquals("Andi", sortedPeople.get(0).name); Assert.assertEquals(2, sortedPeople.get(0).age);
Assert.assertEquals("Barry", sortedPeople.get(1).name); Assert.assertEquals(20, sortedPeople.get(1).age);
Assert.assertEquals("Barry", sortedPeople.get(2).name); Assert.assertEquals(50, sortedPeople.get(2).age);
Assert.assertEquals("Bob", sortedPeople.get(3).name); Assert.assertEquals(40, sortedPeople.get(3).age);
Assert.assertEquals("Bob", sortedPeople.get(4).name); Assert.assertEquals(42, sortedPeople.get(4).age);
Assert.assertEquals("Bob", sortedPeople.get(5).name); Assert.assertEquals(72, sortedPeople.get(5).age);
Assert.assertEquals("Cathy", sortedPeople.get(6).name); Assert.assertEquals(40, sortedPeople.get(6).age);
Assert.assertEquals("Dan", sortedPeople.get(7).name); Assert.assertEquals(4, sortedPeople.get(7).age);
Assert.assertEquals("Debby", sortedPeople.get(8).name); Assert.assertEquals(3, sortedPeople.get(8).age);
// Andi : 2
// Barry : 20
// Barry : 50
// Bob : 40
// Bob : 42
// Bob : 72
// Cathy : 40
// Dan : 4
// Debby : 3
}
/**
* A person in our system.
*/
public static class Person
{
/**
* Creates a new person.
* #param name The name of the person.
* #param age The age of the person.
*/
public Person(String name, int age)
{
this.age = age;
this.name = name;
}
/**
* The name of the person.
*/
public String name;
/**
* The age of the person.
*/
public int age;
#Override
public String toString()
{
if (name == null) return super.toString();
else return String.format("%s : %d", this.name, this.age);
}
}
Using the Java 8 Streams approach, with method references on the getters...
// Create a stream...
var sortedList = persons.stream()
// sort it (does not sort the original list)...
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
// and collect to a new list
.collect(Collectors.toList());
Collection to an array ist also possible:
persons.stream()
.sorted(Comparator.comparing(Person::getName)
.thenComparing(Person::getAge));
.toArray(String[]::new);
And the Java 8 Lambda approach...
//Sorts the original list Lambda style
persons.sort((p1, p2) -> {
if (p1.getName().compareTo(p2.getName()) == 0) {
return p1.getAge().compareTo(p2.getAge());
} else {
return p1.getName().compareTo(p2.getName());
}
});
Lastly...
// This syntax is similar to the Streams example above, but sorts the original list!!!
persons.sort(Comparator.comparing(Person::getName).thenComparing(Person::getAge));
You need to implement your own Comparator, and then use it: for example
Arrays.sort(persons, new PersonComparator());
Your Comparator could look a bit like this:
public class PersonComparator implements Comparator<? extends Person> {
public int compare(Person p1, Person p2) {
int nameCompare = p1.name.compareToIgnoreCase(p2.name);
if (nameCompare != 0) {
return nameCompare;
} else {
return Integer.valueOf(p1.age).compareTo(Integer.valueOf(p2.age));
}
}
}
The comparator first compares the names, if they are not equals it returns the result from comparing them, else it returns the compare result when comparing the ages of both persons.
This code is only a draft: because the class is immutable you could think of building an singleton of it, instead creating a new instance for each sorting.
Have your person class implement Comparable<Person> and then implement the compareTo method, for instance:
public int compareTo(Person o) {
int result = name.compareToIgnoreCase(o.name);
if(result==0) {
return Integer.valueOf(age).compareTo(o.age);
}
else {
return result;
}
}
That will sort first by name (case insensitively) and then by age. You can then run Arrays.sort() or Collections.sort() on the collection or array of Person objects.
Guava's ComparisonChain provides a clean way of doing it. Refer to this link.
A utility for performing a chained comparison statement. For example:
public int compareTo(Foo that) {
return ComparisonChain.start()
.compare(this.aString, that.aString)
.compare(this.anInt, that.anInt)
.compare(this.anEnum, that.anEnum, Ordering.natural().nullsLast())
.result();
}
You can do like this:
List<User> users = Lists.newArrayList(
new User("Pedro", 12),
new User("Maria", 10),
new User("Rafael",12)
);
users.sort(
Comparator.comparing(User::getName).thenComparing(User::getAge)
);
I would be careful when using Guava's ComparisonChain because it creates an instance of it per element been compared so you would be looking at a creation of N x Log N comparison chains just to compare if you are sorting, or N instances if you are iterating and checking for equality.
I would instead create a static Comparator using the newest Java 8 API if possible or Guava's Ordering API which allows you to do that, here is an example with Java 8:
import java.util.Comparator;
import static java.util.Comparator.naturalOrder;
import static java.util.Comparator.nullsLast;
private static final Comparator<Person> COMPARATOR = Comparator
.comparing(Person::getName, nullsLast(naturalOrder()))
.thenComparingInt(Person::getAge);
#Override
public int compareTo(#NotNull Person other) {
return COMPARATOR.compare(this, other);
}
Here is how to use the Guava's Ordering API: https://github.com/google/guava/wiki/OrderingExplained
Create as many comparators as necessary. After, call the method "thenComparing" for each order category. It's a way of doing by Streams. See:
//Sort by first and last name
System.out.println("\n2.Sort list of person objects by firstName then "
+ "by lastName then by age");
Comparator<Person> sortByFirstName
= (p, o) -> p.firstName.compareToIgnoreCase(o.firstName);
Comparator<Person> sortByLastName
= (p, o) -> p.lastName.compareToIgnoreCase(o.lastName);
Comparator<Person> sortByAge
= (p, o) -> Integer.compare(p.age,o.age);
//Sort by first Name then Sort by last name then sort by age
personList.stream().sorted(
sortByFirstName
.thenComparing(sortByLastName)
.thenComparing(sortByAge)
).forEach(person->
System.out.println(person));
Look: Sort user defined object on multiple fields – Comparator (lambda stream)
Use Comparator and then put objects into Collection, then Collections.sort();
class Person {
String fname;
String lname;
int age;
public Person() {
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFname() {
return fname;
}
public void setFname(String fname) {
this.fname = fname;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public Person(String fname, String lname, int age) {
this.fname = fname;
this.lname = lname;
this.age = age;
}
#Override
public String toString() {
return fname + "," + lname + "," + age;
}
}
public class Main{
public static void main(String[] args) {
List<Person> persons = new java.util.ArrayList<Person>();
persons.add(new Person("abc3", "def3", 10));
persons.add(new Person("abc2", "def2", 32));
persons.add(new Person("abc1", "def1", 65));
persons.add(new Person("abc4", "def4", 10));
System.out.println(persons);
Collections.sort(persons, new Comparator<Person>() {
#Override
public int compare(Person t, Person t1) {
return t.getAge() - t1.getAge();
}
});
System.out.println(persons);
}
}
Or you can exploit the fact that Collections.sort() (or Arrays.sort()) is stable (it doesn't reorder elements that are equal) and use a Comparator to sort by age first and then another one to sort by name.
In this specific case this isn't a very good idea but if you have to be able to change the sort order in runtime, it might be useful.
You can use generic serial Comparator to sort collections by multiple fields.
import org.apache.commons.lang3.reflect.FieldUtils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* #author MaheshRPM
*/
public class SerialComparator<T> implements Comparator<T> {
List<String> sortingFields;
public SerialComparator(List<String> sortingFields) {
this.sortingFields = sortingFields;
}
public SerialComparator(String... sortingFields) {
this.sortingFields = Arrays.asList(sortingFields);
}
#Override
public int compare(T o1, T o2) {
int result = 0;
try {
for (String sortingField : sortingFields) {
if (result == 0) {
Object value1 = FieldUtils.readField(o1, sortingField, true);
Object value2 = FieldUtils.readField(o2, sortingField, true);
if (value1 instanceof Comparable && value2 instanceof Comparable) {
Comparable comparable1 = (Comparable) value1;
Comparable comparable2 = (Comparable) value2;
result = comparable1.compareTo(comparable2);
} else {
throw new RuntimeException("Cannot compare non Comparable fields. " + value1.getClass()
.getName() + " must implement Comparable<" + value1.getClass().getName() + ">");
}
} else {
break;
}
}
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return result;
}
}
Arrays.sort(persons, new PersonComparator());
import java.util.Comparator;
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
if(null == o1 || null == o2 || null == o1.getName() || null== o2.getName() ){
throw new NullPointerException();
}else{
int nameComparisonResult = o1.getName().compareTo(o2.getName());
if(0 == nameComparisonResult){
return o1.getAge()-o2.getAge();
}else{
return nameComparisonResult;
}
}
}
}
class Person{
int age; String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Updated version:
public class PersonComparator implements Comparator<? extends Person> {
#Override
public int compare(Person o1, Person o2) {
int nameComparisonResult = o1.getName().compareToIgnoreCase(o2.getName());
return 0 == nameComparisonResult?o1.getAge()-o2.getAge():nameComparisonResult;
}
}
For a class Book like this:
package books;
public class Book {
private Integer id;
private Integer number;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getNumber() {
return number;
}
public void setNumber(Integer number) {
this.number = number;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "book{" +
"id=" + id +
", number=" + number +
", name='" + name + '\'' + '\n' +
'}';
}
}
sorting main class with mock objects
package books;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Main {
public static void main(String[] args) {
System.out.println("Hello World!");
Book b = new Book();
Book c = new Book();
Book d = new Book();
Book e = new Book();
Book f = new Book();
Book g = new Book();
Book g1 = new Book();
Book g2 = new Book();
Book g3 = new Book();
Book g4 = new Book();
b.setId(1);
b.setNumber(12);
b.setName("gk");
c.setId(2);
c.setNumber(12);
c.setName("gk");
d.setId(2);
d.setNumber(13);
d.setName("maths");
e.setId(3);
e.setNumber(3);
e.setName("geometry");
f.setId(3);
f.setNumber(34);
b.setName("gk");
g.setId(3);
g.setNumber(11);
g.setName("gk");
g1.setId(3);
g1.setNumber(88);
g1.setName("gk");
g2.setId(3);
g2.setNumber(91);
g2.setName("gk");
g3.setId(3);
g3.setNumber(101);
g3.setName("gk");
g4.setId(3);
g4.setNumber(4);
g4.setName("gk");
List<Book> allBooks = new ArrayList<Book>();
allBooks.add(b);
allBooks.add(c);
allBooks.add(d);
allBooks.add(e);
allBooks.add(f);
allBooks.add(g);
allBooks.add(g1);
allBooks.add(g2);
allBooks.add(g3);
allBooks.add(g4);
System.out.println(allBooks.size());
Collections.sort(allBooks, new Comparator<Book>() {
#Override
public int compare(Book t, Book t1) {
int a = t.getId()- t1.getId();
if(a == 0){
int a1 = t.getNumber() - t1.getNumber();
return a1;
}
else
return a;
}
});
System.out.println(allBooks);
}
}
I'm not sure if it's ugly to write the compartor inside the Person class in this case. Did it like this:
public class Person implements Comparable <Person> {
private String lastName;
private String firstName;
private int age;
public Person(String firstName, String lastName, int BirthDay) {
this.firstName = firstName;
this.lastName = lastName;
this.age = BirthDay;
}
public int getAge() {
return age;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
#Override
public int compareTo(Person o) {
// default compareTo
}
#Override
public String toString() {
return firstName + " " + lastName + " " + age + "";
}
public static class firstNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.firstName.compareTo(o2.firstName);
}
}
public static class lastNameComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.lastName.compareTo(o2.lastName);
}
}
public static class ageComperator implements Comparator<Person> {
#Override
public int compare(Person o1, Person o2) {
return o1.age - o2.age;
}
}
}
public class Test {
private static void print() {
ArrayList<Person> list = new ArrayList();
list.add(new Person("Diana", "Agron", 31));
list.add(new Person("Kay", "Panabaker", 27));
list.add(new Person("Lucy", "Hale", 28));
list.add(new Person("Ashley", "Benson", 28));
list.add(new Person("Megan", "Park", 31));
list.add(new Person("Lucas", "Till", 27));
list.add(new Person("Nicholas", "Hoult", 28));
list.add(new Person("Aly", "Michalka", 28));
list.add(new Person("Adam", "Brody", 38));
list.add(new Person("Chris", "Pine", 37));
Collections.sort(list, new Person.lastNameComperator());
Iterator<Person> it = list.iterator();
while(it.hasNext())
System.out.println(it.next().toString());
}
}

Collections.sort - I have created 2 user define objects [both are of different types] and then add into arraylist , then I have tried to sort it

Expected Result of code is ClassCastException but Actual Result :- [Person with pid- 1 - a1-name, Person with pid- 2 - c2-name, Sorting.Employee#cdfc9c, Sorting.Employee#1837697]
Person class:
package Sorting;
public class Person implements Comparable<Person> {
private int pid;
private String pname;
public int getPid() {
return pid;
}
public void setPid(int pid) {
this.pid = pid;
}
public String getPname() {
return pname;
}
public void setPname(String pname) {
this.pname = pname;
}
#Override
public String toString() {
return "Person with pid- " + getPid() + " - " + getPname();
}
#Override
public int compareTo(Person p) {
return this.pid - p.pid;
}
}
Employee class:
package Sorting;
public class Employee implements Comparable {
#Override
public int compareTo(Object o) {
return 0;
}
}
SortingofObjects class:
package Sorting;
import java.util.ArrayList;
import java.util.Collections;
public class SortingofObjects {
public static void main(String[] args) {
Person p1 = new Person();
p1.setPid(1);
p1.setPname("a1-name");
Person p2 = new Person();
p2.setPid(2);
p2.setPname("c2-name");
Employee e1 = new Employee();
Employee e2 = new Employee();
ArrayList a = new ArrayList();
a.add(p1);
a.add(p2);
a.add(e1);
a.add(e2);
Collections.sort(a);
System.out.println(a);
}
}
Collections.sort does not call compareTo on every pair in the List, just enough pairs to sort the List. As an example, run this code:
public class Test implements Comparable<Test> {
public static void main(String[] args) {
List<Test> list = new ArrayList<Test>();
list.add(new Test(1));
list.add(new Test(2));
list.add(new Test(3));
list.add(new Test(4));
Collections.sort(list);
}
private final int number;
Test(int number) {
this.number = number;
}
#Override
public int compareTo(Test that) {
System.out.println(this + ".compareTo(" + that + ")");
return 0;
}
#Override
public String toString() {
return "" + number;
}
}
The output is
2.compareTo(1)
3.compareTo(2)
4.compareTo(3)
Since your List is in the order Person, Person, Employee, Employee, the only combination that would throw a ClassCastException, namely
Person.compareTo(Employee)
never occurs. If your List contained an Employee before a Person it would throw an exception.
If it just so happens that the sorting algorithm used only compares Employees to Persons, and not the other way around, then it'll never throw, because Employee.compareTo accepts any Object. You just got lucky, more or less.

How to sort a List of Persons by first name, last name and so on?

I'm prompting the user to enter his first name, last name and age.
I use a confirm dialog to ask user if he want's to add another person then I display the names that he entered.
How can I sort the persons by first name, second name and so on ?
public static void main(String[] args) {
ArrayList<Person> list = new ArrayList();
int i = 0;
while (i == 0) {
Person p = new Person();
String fname = JOptionPane.showInputDialog("Enter First Name ");
String lname = JOptionPane.showInputDialog("Enter Last Name ");
int Age = Integer.parseInt(JOptionPane.showInputDialog("Enter your Age "));
p.setfName(fname);
p.setLname(lname);
p.setAge(Age);
list.add(p);
i=JOptionPane.showConfirmDialog(null, "Are you Want to Add other person","Confirmation",2);
}
for (int j = 0; j < list.size(); j++) {
System.out.print("First name : "+list.get(j).getfName()+"\t Last Name : " + list.get(j).getLname()+"\t Age : "+list.get(j).getAge());
list.equals(j);
System.out.print("\n------------------------ \n ");
}
public class Person{
private String fName;
private String lname;
private int age;
public Person() {
}
public Person(String fName, String lname, int age) {
this.fName = fName;
this.lname = lname;
this.age = age;
}
public String getfName() {
return fName;
}
public void setfName(String fName) {
this.fName = fName;
}
public String getLname() {
return lname;
}
public void setLname(String lname) {
this.lname = lname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
Two ways of doing it are, either have Person implement Comparable or use Comparator
Using Comparator:
Collections.sort(list, new Comparator() {
public int compare(Object o1, Object o2) {
String fn1 = ((Person) o1).getfName();
String fn2 = ((Person) o2).getfName();
int res = fn1.compareTo(fn2);
if (res != 0) {
return res;
} else {
String ln1 = ((Person) o1).getLName();
String ln2 = ((Person) o2).getLName();
return ln1.compareTo(ln2);
}
});
(Just add this before your for loop)
You can figure out how to use Comparable yourself now
If you are using Java 8, you can use Comparator.comparing to specify what method to use for comparison, and thenComparing to specify what method to use in case of a tie, and so on.
Collections.sort(persons, Comparator.comparing(Person::getfName)
.thenComparing(Person::getLname)
.thenComparing(Person::getAge));
Or make Person a Comparable using the same approach:
public class Person implements Comparable<Person> {
// ... more code
#Override
public int compareTo(Person other) {
return Comparator.comparing(Person::getfName)
.thenComparing(Person::getLname)
.thenComparing(Person::getAge)
.compare(this, other);
}
}
Concatenate all parts of the name and sort using them them. Implement a Comparable interface in a Person class.
public class Person implements Comparable<Person>{
...
int compareTo(Person o){
return (fName + lname).compareTo(o.getfName() + o.getLname());
}
...
}
Then you can pass list of such objects to Collections.sort. If you have access to Person's class code it's better to use Comparable interface. Distinct comparator is useful when you are working with 3rd party libraries and you can't add implementation of a new interface.

Generics: java.lang.String cannot be applied to Student

I am in class and my professor is saying her code works and there is nothing wrong with it and that it must be me. I have looked over her code and copied it word for word like she stated however I am still receiving the error:
Pair.java:28: set(java.lang.String,java.lang.Double) in Pair cannot be applied to (Student,java.lang.Double)
The part I bolded is the part I receive an error on. Are the set methods incorrect? Because the line with the error goes back to the set methods
This is her code:
Student.java
import java.io.*;
public class Student implements Person {
String id;
String name;
int age;
//constructor
public Student(String i, String n, int a) {
id = i;
name = n;
age = a;
}
public String getID() {
return id;
}
public String getName() {
return name; //from Person interface
}
public int getAge() {
return age; //from Person interface
}
public void setid(String i) {
this.id = i;
}
public void setName(String n) {
this.name = n;
}
public void setAge(int a) {
this.age = a;
}
public boolean equalTo(Person other) {
Student otherStudent = (Student) other;
//cast Person to Student
return (id.equals(otherStudent.getID()));
}
public String toString() {
return "Student(ID: " + id +
",Name: " + name +
", Age: " + age +")";
}
}
Person.java
import java.io.*;
public interface Person {
//is this the same person?
public boolean equalTo (Person other);
//get this persons name
public String getName();
//get this persons age
public int getAge();
}
Pair.java
import java.io.*;
public class Pair<K, V> {
K key;
V value;
public void set (K k, V v) {
key = k;
value = v;
}
public K getKey() {return key;}
public V getValue() {return value;}
public String toString() {
return "[" + getKey() + "," + getValue() + "]";
}
public static void main (String [] args) {
Pair<String,Integer> pair1 =
new Pair<String,Integer>();
pair1.set(new String("height"),new
Integer(36));
System.out.println(pair1);
Pair<String,Double> pair2 =
new Pair<String,Double>();
//class Student defined earlier
**pair2.set(new Student("s0111","Ann",19),**
new Double(8.5));
System.out.println(pair2);
}
}
For the instantiation:
Pair<String,Double> pair2 = new Pair<String,Double>();
your set() method signature is equivalent to: set(String, Double). And you are passing it a Student reference in the below invocation, which wouldn't work, as Student is not a String.
pair2.set(new Student("s0111","Ann",19), new Double(8.5));
To avoid the issue, change the declaration of pair2 to:
Pair<Student,Double> pair2 = new Pair<Student,Double>();
The error is pretty self explanatory. pair2 is defined as a Pair<String, Double>. You're trying to set a Student, Double. That won't work.
Pair<String,Double> pair2 = new Pair<String,Double>();
should be:
Pair<Student,Double> pair2 = new Pair<Student,Double>();
from your code pair2 is defined as type Pair<String,Double>,i.e pair2 set() method expecting String ,Double as arguments,but you are passing Student , Double.
So
Pair<String,Double> pair2 = new Pair<String,Double>();
should be
Pair<Student,Double> pair2 = new Pair<Student,Double>();

Categories