Comparator implementation for sorting the collection [duplicate] - java

This question already has answers here:
How are Anonymous inner classes used in Java?
(18 answers)
Closed 7 years ago.
Recently I'm trying to get more familiar with the Comparator interface in Java. I have an exercise which is about to sort the ArrayList of strings from the shortest to longest. I used a Comparator of Strings. When searching the net, I found the following solution proposal:
public static Comparator<String> lengthComparator = new Comparator<String>() {
#Override
public int compare(String a, String b) {
if (a.length() == b.length()) {
return a.compareTo(b);
} else {
return (a.length() > b.length() ? 1 : -1);
}
}
};
Then I used it in my code to sort the set:
Collections.sort(set, lengthComparator);
And it worked. What I'd like to ask is the specific way of defining the lengthComparator object here. We create a new object:
new Comparator<String>()
with the default constructor. But then there is a further code with overwritten method in "{}" brackets. Is it a normal way of creating objects? I've never met it before and I'd like to learn more about it. Could you please advise me some referal materials where I can find more informations about it?

Yes this is a common way to create objects, it is called anonymous class.
Comparator is an interface, and you want a class to be instantiated, so you create an object from an anonymous class that implements Comparator.
Anonymous class example
public void sortSetByStringLength(Set set) {
Comparator<String> lengthComparator = new Comparator<String>() {
#Override
public int compare(String a, String b) {
if (a.length() == b.length()) {
return a.compareTo(b);
} else {
return (a.length() > b.length() ? 1 : -1);
}
}
}
Collections.sort(set, lengthComparator);
}
Regular class example
public class LengthComparator implements Comparator<String> {
#Override
public int compare(String a, String b) {
if (a.length() == b.length()) {
return a.compareTo(b);
} else {
return (a.length() > b.length() ? 1 : -1);
}
}
}
And in your program sort a list this way :
public void sortSetByStringLength(Set set) {
Collections.sort(set, new LengthComparator());
}

I'm not sure what's the exact question but as it was already said :
What you have instanciated is an anonymous class. Indeed, as you already know an interface cannot be instanciated.
So, when you do :
new Comparator<String>()
{
#Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return 0;
}
});
it is like you've created a class called let's say : LengthComparator like this :
public class LengthComparator implements Comparator<String>{
public LengthComparator()
{
}
#Override
public int compare(String o1, String o2) {
int ack = 0;
if(o1 == null && o2 == null){
ack = 0;
}
else if(o1 != null && o2 == null){
// Decide what you should do here !
// returnValue = ???
}
else if(o1 == null && o2 != null){
// Decide here, too !
// returnValue = ???
}
else{
if(o1.length() == o2.length()){
// Sort by order ...
ack = o1.compareTo(o2);
}
else{
ack = o1.length() > o2.length() ? 1 : -1;
}
}
return ack;
}
}
As a personal advice, always check your arguments toward null values as you don't really know what is going to be passed in...

Related

Comparator.nullsLast does not avoid NullPointerException

I want to sort a list of objects by one of nullable fields.
In order to avoid NullPointerexception I use Comparator.nullsLast. But the exception still occurs:
public class Test {
public static void main(String[] args) {
List<Bean> l = new ArrayList<>();
for (int i=0;i<5;i++) {
Bean b = new Bean("name_"+i,i);
l.add(b);
}
l.get(2).setVal(null);
System.out.println(l);
Collections.sort(l, Comparator.nullsLast(Comparator.comparing(Bean::getVal)));
System.out.println(l);
}
static class Bean{
String name;
Integer val;
// omit getters & setters & constructor
}
}
How can I sort this kind of list?
You should use Comparator.nullsLast twice:
list.sort(nullsLast(comparing(Bean::getVal, nullsLast(naturalOrder()))));
First nullsLast will handle the cases when the Bean objects are null.
Second nullsLast will handle the cases when the return value of Bean::getVal is null.
In case you're sure there aren't any null values in your list then you can omit the first nullsLast (as noted by #Holger) :
list.sort(comparing(Bean::getVal, nullsLast(naturalOrder())));
You can possibly use :
Collections.sort(l, Comparator.comparing(Bean::getVal,
Comparator.nullsLast(Comparator.naturalOrder())));
You can review this example
#Override
public int compare(Example o1, Example o2) {
if (o1 == null && o2 == null) return 0;
if (o1 == null) return -1;
if (o2 == null) return 1;
return Comparator.comparing(Example::getSta,
Comparator.nullsLast(Comparator.naturalOrder()))
.thenComparing(Example::getId,
Comparator.nullsLast(Comparator.naturalOrder()))
.compare(o1, o2);
}
class Example {
String sta; Long id;}

How to replace < & > to string

What can I replace the < and > with because i want to compare string below one is compare int, so got any solution to solve this question and doesnt change the function.
public int compareTo(Object o) {
Patient p = (Patient) o;
if (this.getCategory() < p.getCategory())
return -1;
if (this.getCategory() > p.getCategory())
return 1;
else {
if (this.getTimeArrived().before(p.getTimeArrived()))
return -1;
if (this.getTimeArrived().after(p.getTimeArrived()))
return 1;
}
return 0;
}
How about this? can change the > & < to another solution because i want to compare with string
import java.util.Comparator;
public class PatientComparator implements Comparator<Patient>{
public int compare(Patient p1, Patient p2) {
if (p1.getCategory() < p2.getCategory())
return -1;
if (p1.getCategory() > p2.getCategory())
return 1;
else { if (p1.getTimeArrived().before(p2.getTimeArrived()))
return -1;
if (p1.getTimeArrived().after(p2.getTimeArrived()))
return 1;
}
return 0;
}
}
From the additional information you provided (currently inside an answer) getCategory() returns a String and getTimeArrived() returns a java.util.Date. Your goal seems to be: Compare by "category" and, if equal, then compare by "time arrived".
Both String and Date implement the Comparable interface, so you can do something like this:
public int compareTo(Patient other) {
// This code doesn't handle nulls
int result = getCategory().compareTo(other.getCategory());
if (result == 0) {
result = getTimeArrived().compareTo(other.getTimeArrived());
}
return result;
}
You could also create a Comparator.
Comparator<Patient> c = Comparator.comparing(Patient::getCategory)
.thenComparing(Patient::getArrivedTime);
Also, you are creating a compareTo method without Patient implementing Comparable. You should change it to:
public class Patient implements Comparable<Patient> { /* code */ }
Then override the compareTo method declared in Comparable. This also forces you to use compareTo(Patient) rather than compareTo(Object).

How to sort a string list according to equality to a string?

I have a big string list of names and an autofill function for a combobox.
Currently i'm just filtering according to contains.
But the order in the combobox isn't nice.
Is there a easy way to sort the string list (after contains) according to equality to a string?
Example:
Currently:
combobox input -> Neustadt
List<String> 1. Bad Neustadt an der Saale
2. Bremen-Neustadt
3. ...
n. Neustadt
n+1. Neustadt / Pelzerhaken
Wandted:
1. Neustadt
2. Neustadt / Pelzerhaken
3. ...
n. Bremen-Neustadt
EDIT
Based on Petr Gladkikh's answer my solution is following method:
public static List<String> sortListCompairedToEquality(List<String> list, String str) {
if (list == null || str == null)
return null;
final String compStr = str.toLowerCase();
Collections.sort(list, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
if (s1.equals(compStr) && !s2.equals(compStr)) {
return -1;
} else if (!s1.equals(compStr) && s2.equals(compStr)) {
return 1;
} else if (s1.indexOf(compStr) < s2.indexOf(compStr)) {
return -1;
} else if (s1.indexOf(compStr) > s2.indexOf(compStr)) {
return 1;
}
return 0;
}
});
return list;
}
It is not clear what rules of "equality to a string" you have in mind. Generally if you want to sort according to some rules use 'sort' method with custom comparator:
java.util.Collections.sort(listOfStrings, new Comparator<String>() {
#Override
public int compare(String s1, String s2) {
return 0; // Your comparison logic goes here
}
});
See javadoc for java.util.Comparator interface for details.

Multi column order using java

Hi I need any method or explanation on how we can order multiple column values in java.
The output should be similar to the multiple column order output in MySQL.
for clarification please check the below link
http://www.dynamicdrive.com/forums/showthread.php?19797-ORDER-BY-Multiple-Columns
Let's say your object looks like this:
public DataObject {
public String name;
public int age;
public String hairColour;
}
Let's say you want to sort them based on age, then hair colour, then name. You could create a comparator as follows:
public DataObjectComparator extends Comparator<DataObject> {
public int compare(DataObject o1, DataObject o2) {
// if the age is the same
if(o1.age == o2.age) {
// if the hair colour is the same
if(o1.hairColour.compareTo(o2.hairColour) == 0) {
// return the name comparison
return o1.name.compareTo(o2.name);
} else { // else return the hair colour comparison
return o1.hairColour.compareTo(o2.hairColour);
}
} else { // else return the age comparison
return o1 < o2 ? -1 : 1;
}
}
}
You can sort arraylist for multiple properties using below sample comparator.
public class CustomeClass implements Comparator<CustomeObject> {
public int compare(CustomeObject o1, CustomeObject o2) {
int value1 = o1.prop1.compareTo(o2.prop1);
if (value1 == 0) {
int value2 = o1.prop2.compareTo(o2.prop2);
if (value2 == 0) {
return o1.prop3.compareTo(o2.prop3);
} else {
return value2;
}
return value1;
}
}
Basically it continues comparing each successive attribute of your class whenever the compared attributes so far are equal (== 0).

Getting Max value from x.y.z formatted Number

Is there an easy way of finding the MAX number from the list where number is stored in x.y.z format? e.g. To manage some system versions.
I have tried Collection.max(list) and that does not work.
Sample Code:
public static void main(String args[])
{
List<String> list = new ArrayList<String>();
list.add("1.0.0");
list.add("1.1.0");
list.add("1.9.0");
list.add("1.10.0");
System.out.println(Collections.max(list));
}
Expected: 1.10.0
Result: 1.9
Thanks for your time.
Try to use this one :
Collections.max(myList, new Comparator<String>() {
#Override
public int compare(String lhs, String rhs) {
String[] first = lhs.split("\\.");
String[] second = rhs.split("\\.");
for (int i = 0; i < first.length; i++) {
if(Integer.valueOf(first[i]) > Integer.valueOf(second[i])) {
return 1;
}
if(Integer.valueOf(first[i]) < Integer.valueOf(second[i])) {
return -1;
}
}
return 0;
}
});
Well for one thing, you need to ensure that Java knows they are numbers - at the moment they're just Strings, and strings sort lexigraphically (i.e. in "alphabetical order").
My approach to this would be to create a small class that implements Comparable, which will then work automatically with sorting and comparison logic. Something like this perhaps:
public class VersionNumber implements Comparable<VersionNumber> {
public final int major;
public final int minor;
public final int patch;
// Constructor etc. elided
public int compareTo(VersionNumber other) {
if (other.major != major) return major - other.major;
if (other.minor != minor) return minor - other.minor;
return patch - other.patch;
}
}
Parsing the string to create instances of this class is left as an exercise to the reader!
You may have to write a custom Comparator for comparing version number strings:
public class VersionComparator extends Comparator<String> {
#Override
public int compare(String o1, String o2) {
// Get major/minor/revison numbers by splitting strings at dots
String[] p1 = o1.split("\\.");
String[] p2 = o2.split("\\.");
// Compare major versions then minor then revision until a difference found
for(int i = 0; i < (p1.length < p2.length) ? p1.length : p2.length; i++) {
int result = Integer.valueOf(p1[i]).compareTo(Integer.valueOf(p2[i]));
if(result != 0) return result;
}
// Return zero if they're identical
return 0;
}
}
The you can use this comparator with the Collections.max function:
Collections.max(list, new VarsionComparator());
You can use version of max with the specified comparator:
System.out.println(Collections.max(list, new Comparator<String>() {
public int compare(String s1, String s2)
{
StringTokenizer st1 = new StringTokenizer(s1,".");
StringTokenizer st2 = new StringTokenizer(s2,".");
int res = 0;
String t1, t2;
while(st1.hasMoreTokens() && st2.hasMoreTokens())
{
t1 = st1.nextToken();
t2 = st2.nextToken();
res = Integer.valueOf(t1).compareTo(Integer.valueOf(t2));
}
if(res == 0)
{
res = st1.hasMoreTokens() ? 1 : (st2.hasMoreTokens() ? -1 : 0);
}
return res;
}
public boolean equals(Object obj) { return false; }
}));
This will give you 1.9 because it will not consider second number to be 10, it will treat it as 1 first and then 9
Edit
If you want to do it manually, then
Split your number on basis of "."
Check manually which number is greater.

Categories