The issue I'm having is that the get method throws NPE when the key is a different instance than the one in the TreeMap.
public class ConjuntDocuments {
private TreeMap<Capcalera, Document> almacen;
private ArrayList<Pair_plantilla> plantilla;
ConjuntDocuments() {
almacen = new TreeMap<Capcalera, Document>(new CustomComparator());
plantilla = new ArrayList<Pair_plantilla>();
}
private static class CustomComparator implements Comparator<Capcalera> {
#Override
public int compare(Capcalera c1, Capcalera c2) {
int ax = c1.get_tit().get_nom().compareFrase(c2.get_tit().get_nom());
if (ax < 0) return -1;
else if (ax > 0) return 1;
//titols iguals
else {
ax = c1.get_au().get_nom().compareFrase(c2.get_au().get_nom());
if (ax < 0) return -1;
else if (ax > 0) return 1;
}
//titols i autors iguals
return 0;
}
}
compareFrase compares ArrayLists(Paraula) -> Frase, Paraula is like a string, get_chars returns a String.
public int compareFrase(Frase f) {
for(int i=0; i<min(this.get_size(), f.get_size()); ++i){
int aux = this.get_paraula(i).get_chars().compareTo(f.get_paraula(i).get_chars());
if(aux < 0) return -1;
else if(aux > 0) return 1;
}
if(this.get_size() < f.get_size()) return -1;
else if(this.get_size() > f.get_size()) return 1;
return 0;
}
Titol and autor are Frases -> ArrayList(Paraula)
public class Capcalera {
private Titol tit;
private Autor au;
So after trying to figure this out, I've realised that the get method only works if the key referenced is the same instance than the one mapped, right after putting an entry (almacen.put(capcalera,document) , if I try to call almacen.get(Capcalera) it will return the value correctly, but if I create a new Capcalera, it will throw NPE. I'm assuming there is an issue with the comparator but since the entries are sorted correctly I can't figure out what is wrong.
EDIT:
I've implemented .equals and .hashcode from Capcalera, but I might be doing something wrong because .get from the Treemap still throws NPE.
#Override
public int hashCode() {
int hashTitol = tit != null ? tit.hashCode() : 0;
int hashAutor = au != null ? au.hashCode() : 0;
return (hashTitol + hashAutor) * hashAutor + hashTitol;
}
#Override
public boolean equals(Object other) {
if (other instanceof Capcalera) {
Capcalera otherCapcalera = (Capcalera) other;
return
(( this.get_tit().get_nom().equalsFrase(otherCapcalera.get_tit().get_nom()) ||
( this.get_tit() != null && otherCapcalera.get_tit() != null &&
this.get_tit().get_nom().equalsFrase(otherCapcalera.get_tit().get_nom()) )) &&
( this.get_au().get_nom().equalsFrase(otherCapcalera.get_au().get_nom()) ||
( this.get_au() != null && otherCapcalera.get_au() != null &&
this.get_au().get_nom().equalsFrase(otherCapcalera.get_au().get_nom()))) );
}
return false;
}
equalsFrase returns true if Titol/Autor are equals
public boolean equalsFrase(Frase f) {
for(int i=0; i<min(this.get_size(), f.get_size()); ++i){
int aux = this.get_paraula(i).get_chars().compareTo(f.get_paraula(i).get_chars());
if(aux < 0) return false;
else if(aux > 0) return false;
}
if(this.get_size() < f.get_size()) return false;
else if(this.get_size() > f.get_size()) return false;
return true;
}
Related
I am trying to implement an algorithm that checks if an array of comparable elements is increasing or decreasing. So far I have written this method:
class Solution {
public boolean isMonotonic(int[] A) {
int store = 0;
for (int i = 0; i < A.length - 1; ++i) {
int c = Integer.compare(A[i], A[i+1]);
if (c != 0) {
if (c != store && store != 0)
return false;
store = c;
}
}
return true;
}
}
I changed the method signature, by passing a generic method of comparables, but I'm struggling to implement the compareTo method. I am probably using the bounded generics wrong, but I'm not too sure?
My attempt:
public boolean Test(T[] n)
{
if (n.length < 3)
return true;
int direction = n[0].compareTo(n[1]);
for (int i = 1; i < n.length-1; i++){
int step = n[i].compareTo(n[i+1]);
if (step == 0)
continue;
if (direction == 0)
direction = step;
else if ( sdtep < 0 && direction > 0
|| step > 0 && direction < 0)
return false;
}
return true;
}
In order to make your method take a generic argument, change its header:
public static <T extends Comparable<? super T>> boolean isMonotonic(T[] A)
You can then compare items of the array using the Comparable::compareTo method:
int c = A[i].compareTo(A[i+1]);
Monotone function:
public static boolean isMonotone(int[] a) {
boolean monotone = true;
int i=0;
while(a[i]==a[i+1]) {
i++;
}
if(a[i]>a[i+1]) {
for(int j=0;j<(a.length-1);j++) {
if(!(a[j]>=a[j+1])){
monotone=false;
}
}
}
if(a[i]<a[i+1]) {
for(int j=0;j<(a.length-1);j++) {
if(!(a[j]<=a[j+1])){
monotone=false;
}
}
}
return monotone;
}
Strictly monotone function:
public static boolean isMonotone(int[] a) {
boolean monotone = true;
if(a[0]>a[1]) {
for(int i=0;i<(a.length-1);i++) {
if(!(a[i]>a[i+1])){
monotone=false;
}
}
}
if(a[0]<a[1]) {
for(int i=0;i<(a.length-1);i++) {
if(!(a[i]<a[i+1])){
monotone=false;
}
}
}
if(a[0]==a[1]) return false;
return monotone;
}
i have a class in a java program where i am using a toString function to retrieve data. the toString checks a private function in the same class which returns a int value, for displaying different types of return messages.~
The problem is that if i use a local variable in the string function every turns out good, but if i check in the if statements directlly the private function, this function doesnt return any value.
private int computerTryHorizontalPlay() {
int repeatedMyValueCount = 0;
int repeatedYourValueCount = 0;
int[] myPositions = new int[3];
int[] yourPositions = new int[3];
for (int a = 0; a < 3; a++) {
int repeatedMyValue = 0;
int repeatedYourValue = 0;
int emptyFields = 0;
int[] emptyPosition = new int[2];
for (int b = 0; b < 3; b++) {
if (jogoGalo[a][b] == 'X') {
repeatedMyValue++;
} else if (jogoGalo[a][b] == 'O') {
repeatedYourValue++;
}
if (jogoGalo[a][b] == '-') {
emptyPosition[0] = a;
emptyPosition[1] = b;
emptyFields++;
}
}
if (repeatedMyValue == 3 || repeatedYourValue == 3) {
return 3;
} else {
if (emptyFields == 1) {
if (repeatedMyValue == 2) {
repeatedMyValueCount++;
myPositions[repeatedMyValueCount - 1] = emptyPosition[0];
myPositions[repeatedMyValueCount] = emptyPosition[1];
} else if (repeatedYourValue == 2) {
repeatedYourValueCount++;
yourPositions[repeatedYourValueCount - 1] = emptyPosition[0];
yourPositions[repeatedYourValueCount] = emptyPosition[1];
}
}
}
}
if (repeatedMyValueCount > 0) {
jogoGalo[myPositions[0]][myPositions[1]] = 'X';
return 2;
} else if (repeatedYourValueCount > 0) {
jogoGalo[yourPositions[0]][yourPositions[1]] = 'X';
return 1;
}
return 0;
}
This doesn´t work!
public String toString() {
if(computerTryHorizontalPlay() == 3) {
return "The game has already ended!";
}
else if(computerTryHorizontalPlay() == 2) {
return "Computer won!";
}
else if(computerTryHorizontalPlay() == 1) {
return "Computer defendeu!";
}
return null;
}
This works!
public String toString() {
int horizontalFunctionValue = computerTryHorizontalPlay();
if(horizontalFunctionValue == 3) {
return "The game has already ended!";
}
else if(horizontalFunctionValue == 2) {
return "Computer won!";
}
else if(horizontalFunctionValue == 1) {
return "Computer defendeu!";
}
return null;
}
}
toString() must be a read-only method, i.e. it is not allowed to have side-effects like changing the state of the object. Since computerTryHorizontalPlay() is a state-changing method, you are not allowed to call it from toString().
Since the only state-change happens in the last if statement, you can change the code to not execute the play when called from toString(), like this:
private int computerTryHorizontalPlay() {
return computerTryHorizontalPlay(true);
}
private int computerTryHorizontalPlay(boolean doMove) {
// lots of code here
if (repeatedMyValueCount > 0) {
if (doMove)
jogoGalo[myPositions[0]][myPositions[1]] = 'X';
return 2;
} else if (repeatedYourValueCount > 0) {
if (doMove)
jogoGalo[yourPositions[0]][yourPositions[1]] = 'X';
return 1;
}
return 0;
}
public String toString() {
if(computerTryHorizontalPlay(false) == 3) {
return "The game has already ended!";
}
else if(computerTryHorizontalPlay(false) == 2) {
return "Computer won!";
}
else if(computerTryHorizontalPlay(false) == 1) {
return "Computer defeated!";
}
return null;
}
This question already has answers here:
Why do I need to override the equals and hashCode methods in Java?
(31 answers)
Closed 6 years ago.
Hi I have Two lists of objects :
public class TimeTableForBus {
String bsName;
int bsType;
Time ttTime;
int lon;
int lat;
int ttID;
int bus_stop_status;
}
And I generated two list just like this :
private static ArrayList getList( QueryRunner qRunner, Connection conn){
try {
beans = (List) qRunner.query(conn, "call mpklocal.LCD_GetDispInfoAllTimeTable()",
new BeanListHandler(TimeTableForBus.class));
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
for (int i = 0; i < beans.size(); i++) {
TimeTableForBus bean = (TimeTableForBus) beans.get(i);
// bean.print();
}
ArrayList<TimeTableForBus> bus = new ArrayList<>();
for (int i = 0; i < beans.size(); i++) {
bus.add((TimeTableForBus) beans.get(i));
}
return bus;
}
When I check if are equals I see false when I do this I see false . The list have this same objects:
public static boolean equalLists(List<TimeTableForBus> one, List<TimeTableForBus> two){
if (one == null && two == null){
return true;
}
if((one == null && two != null)
|| one != null && two == null
|| one.size() != two.size()){
return false;
}
//to avoid messing the order of the lists we will use a copy
//as noted in comments by A. R. S.
// one = new ArrayList<String>(one);
// two = new ArrayList<String>(two);
//
// Collections.sort(one);
// Collections.sort(two);
return one.equals(two);
}
public static boolean listsAreEquivelent(List<? extends Object> a, List<? extends Object> b) {
if(a==null) {
if(b==null) {
//Here 2 null lists are equivelent. You may want to change this.
return true;
} else {
return false;
}
}
if(b==null) {
return false;
}
Map<Object, Integer> tempMap = new HashMap<>();
for(Object element : a) {
Integer currentCount = tempMap.get(element);
if(currentCount == null) {
tempMap.put(element, 1);
} else {
tempMap.put(element, currentCount+1);
}
}
for(Object element : b) {
Integer currentCount = tempMap.get(element);
if(currentCount == null) {
return false;
} else {
tempMap.put(element, currentCount-1);
}
}
for(Integer count : tempMap.values()) {
if(count != 0) {
return false;
}
}
return true;
}
And I don't know why I have this result
Try to override public boolean equals(Object o) and public int hashCode() like this:
public class TimeTableForBus {
String bsName;
int bsType;
Time ttTime;
int lon;
int lat;
int ttID;
int bus_stop_status;
#Override
public int hashCode() {
int result = 31;
result = 37 * result + generateHash(bsName);
result = 37 * result + generateHash(bsType);
result = 37 * result + generateHash(ttTime);
result = 37 * result + generateHash(lon);
result = 37 * result + generateHash(lat);
result = 37 * result + generateHash(ttID);
result = 37 * result + generateHash(bus_stop_status);
return result;
}
#Override
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof TimeTableForBus))
return false;
TimeTableForBus model = (TimeTableForBus)o;
return Objects.equals(bsName, model.bsName)
&& bsType == model.bsType
&& Objects.equals(ttTime, model.ttTime)
&& lon == model.lon
&& lat == model.lat
&& ttID == model.ttID
&& bus_stop_status == model.bus_stop_status;
}
private int generateHash(long value) {
return (int)(value ^ (value >>> 32));
}
private int generateHash(Object value) {
return value == null ? 0 : value.hashCode();
}
}
So lets say there is a node, and it has three properties: pdf:Title, dc:title & node name , I would like to compare and sort the array based of these properties with priority in same order. So if node1 has all three properties and node2 has only dc:title it should compare pdf:Title of node1 with dc:titleof node2.
This is the code I am using:
#Override
public int compare(Node o1, Node o2) {
try {
int compareValue;
boolean o1HasPdfTitle = o1.hasProperty("jcr:content/metadata/pdf:Title");
boolean o1HasDcTitle = o1.hasProperty("jcr:content/metadata/dc:title");
boolean o2HasPdfTitle = o2.hasProperty("jcr:content/metadata/pdf:Title");
boolean o2HasDcTitle = o2.hasProperty("jcr:content/metadata/dc:title");
if (o1HasPdfTitle && o2HasPdfTitle) {
compareValue = o1.getProperty("jcr:content/metadata/pdf:Title").getString().compareTo(o2.getProperty("jcr:content/metadata/pdf:Title").getString());
return compareValue;
} else if (o1HasPdfTitle && o2HasDcTitle) {
compareValue = o1.getProperty("jcr:content/metadata/pdf:Title").getString().compareTo(o2.getProperty("jcr:content/metadata/dc:title").getString());
return compareValue;
} else if (o1HasDcTitle && o2HasPdfTitle) {
compareValue = o1.getProperty("jcr:content/metadata/dc:title").getString().compareTo(o2.getProperty("jcr:content/metadata/pdf:Title").getString());
return compareValue;
} else if (o1HasDcTitle && o2HasDcTitle) {
compareValue = o1.getProperty("jcr:content/metadata/dc:title").getString().compareTo(o2.getProperty("jcr:content/metadata/dc:title").getString());
return compareValue;
} else if (!o1HasPdfTitle && !o1HasDcTitle && o2HasPdfTitle){
compareValue = o1.getName().compareTo(o2.getProperty("jcr:content/metadata/pdf:Title").getString());
return compareValue;
} else if (!o1HasPdfTitle && !o1HasDcTitle && o2HasDcTitle){
compareValue = o1.getName().compareTo(o2.getProperty("jcr:content/metadata/dc:title").getString());
return compareValue;
} else if (o1HasPdfTitle && !o2HasPdfTitle && !o2HasDcTitle){
compareValue = o1.getProperty("jcr:content/metadata/pdf:Title").getString().compareTo(o2.getName());
return compareValue;
} else if (o1HasDcTitle && !o2HasPdfTitle && !o2HasDcTitle){
compareValue = o1.getProperty("jcr:content/metadata/dc:title").getString().compareTo(o2.getName());
return compareValue;
} else {
compareValue = o1.getName().compareTo(o2.getName());
return compareValue;
}
} catch (Exception e) {
LOGGER.debug("CustomComparatorTitle debug message" + e);
return 0;
}
Problem: It does not sort correctly, they are not alphabetically ordered. Am I missing something?
Also is there a better way to wright the code without using so many conditions? I was trying to avoid the IllegalArgumentException: Comparison method violates its general contract!
i'm not sure I understand your code, but it's seems its better in this way:
final String pPdf="jcr:content/metadata/pdf:Title";
final String pDC="jcr:content/metadata/dc:title";
String[] values=new String[2];
for (int i = 0; i < values.length; i++)
{
Node curNode=i==0?o1:o2;
if(curNode.hasProperty(pPdf))
values[i]=curNode.getProperty(pPdf).getString();
else if(curNode.hasProperty(pDC))
values[i]=curNode.getProperty(pDC).getString();
else
values[i]=curNode.getName();
}
return values[0].compareTo(values[1]);
it's not better?
I am having this problem for some time, have searched lots of StackOverflow questions but couldn't solve my problem.
I also asked a similar question before and got the suggestion to use,
System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
It didn't solve my problem. I never got this exception on any of my test devices, but some of my users have been reporting it regularly. I am really clueless how to solve it.
The Exception
This is the exception that I am getting,
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeLo(TimSort.java:743)
at java.util.TimSort.mergeAt(TimSort.java:479)
at java.util.TimSort.mergeCollapse(TimSort.java:404)
at java.util.TimSort.sort(TimSort.java:210)
at java.util.TimSort.sort(TimSort.java:169)
at java.util.Arrays.sort(Arrays.java:2023)
at java.util.Collections.sort(Collections.java:1883)
or sometimes this,
java.lang.IllegalArgumentException: Comparison method violates its general contract!
at java.util.TimSort.mergeHi(TimSort.java:864)
at java.util.TimSort.mergeAt(TimSort.java:481)
at java.util.TimSort.mergeCollapse(TimSort.java:406)
at java.util.TimSort.sort(TimSort.java:210)
at java.util.TimSort.sort(TimSort.java:169)
at java.util.Arrays.sort(Arrays.java:2010)
at java.util.Collections.sort(Collections.java:1883)
What I Have Done
enum FileItemComparator implements Comparator<FileItem> {
//Using ENUM
NAME_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 != null && n2 != null)
result = n1.compareTo(n2);
}
return result;
}
},
DATE_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String d1 = o1.getFileDate();
String d2 = o2.getFileDate();
if (d1 != null && d2 != null) {
Long l1 = Long.valueOf(d1);
Long l2 = Long.valueOf(d2);
if (l1 != null && l2 != null) {
result = l1.compareTo(l2);
}
}
}
return result;
}
},
SIZE_SORT {
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
File f1 = o1.getItem();
File f2 = o2.getItem();
if (f1 != null && f2 != null) {
result = Long.valueOf(f1.length()).compareTo(Long.valueOf(f2.length()));
}
}
return result;
}
};
public static Comparator<FileItem> descending(final Comparator<FileItem> other) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
return -1 * other.compare(o1, o2);
}
};
}
public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
for (FileItemComparator option : multipleOptions) {
int result = option.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
};
}
}
This is how I am sorting,
Collections.sort(dirs, FileItemComparator.getComparator(FileItemComparator.NAME_SORT));
The Problem
I am sure there is something wrong in the compare method with transitive dependencies. I have tried a lot and can't seem to fix it. Actually, I never got this problem in any of my test devices, but my users are reporting it constantly.
I hope anyone here will be able to catch the problem and help me solve it once and for all.
Updated Code (Thanks to #Eran)
I thought it would be best to help others by posting the complete updated code. It will help a lot of people facing the same problem.
enum FileItemComparator implements Comparator<FileItem> {
//Using ENUM
NAME_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 == null) {
if (n2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
},
DATE_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String d1 = o1.getFileDate();
String d2 = o2.getFileDate();
if (d1 == null) {
if (d2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (d2 == null) {
return -1;
}
Long l1 = Long.valueOf(d1);
Long l2 = Long.valueOf(d2);
if (l1 == null) {
if (l2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (l2 == null) {
return -1;
}
return l1.compareTo(l2);
}
},
SIZE_SORT {
public int compare(FileItem o1, FileItem o2) {
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
File f1 = o1.getItem();
File f2 = o2.getItem();
if (f1 == null) {
if (f2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (f2 == null) {
return -1;
}
Long l1 = Long.valueOf(f1.length());
Long l2 = Long.valueOf(f2.length());
if (l1 == null) {
if (l2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (l2 == null) {
return -1;
}
return l1.compareTo(l2);
}
};
public static Comparator<FileItem> descending(final Comparator<FileItem> other) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
return -1 * other.compare(o1, o2);
}
};
}
public static Comparator<FileItem> getComparator(final FileItemComparator... multipleOptions) {
return new Comparator<FileItem>() {
public int compare(FileItem o1, FileItem o2) {
for (FileItemComparator option : multipleOptions) {
int result = option.compare(o1, o2);
if (result != 0) {
return result;
}
}
return 0;
}
};
}
}
Let's look at your first compare method :
public int compare(FileItem o1, FileItem o2) {
int result = 0;
if (o1 != null && o2 != null) {
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 != null && n2 != null)
result = n1.compareTo(n2);
}
return result;
}
Suppose you are comparing two FileItems (let's call them o1 and o2), one with a file name and the other without (i.e. null file name). Your method will return 0.
Now if you compare o2 with another FileItem (o3) for which the file name is not null, you return 0 again.
But if you compare o1 to o3, since both of them have non null file name, the comparison returns -1 or 1 (assuming the file names are different).
Therefore your comparison is inconsistent since it's not transitive.
If one element lacks a property required for the comparison and the other doesn't, you shouldn't return 0. You should decide whether to return 1 or -1 (depending whether, for example, the FileItems with null names should be ordered before or after the FileItems with non null names).
For example :
public int compare(FileItem o1, FileItem o2)
{
if (o1 == null) {
if (o2 == null) {
return 0;
} else {
return 1; // this will put null in the end
}
} else if (o2 == null) {
return -1;
}
String n1 = o1.getFileName();
String n2 = o2.getFileName();
if (n1 == null) {
if (n2 == null) {
return 0;
} else {
return 1; // this will put null names after non null names
}
} else if (n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
This is a common mistake with comparators - you are not handling null consistently. The usual pattern would look like this:
public int compare(FileItem o1, FileItem o2) {
// null == null
if (o1 == null && o2 == null) {
return 0;
}
// null < not null
if (o1 == null || o2 == null) {
return -1;
}
// Neither can be null now so this is safe.
String n1 = o1.getFileName();
String n2 = o2.getFileName();
// Same logic again.
if (n1 == null && n2 == null) {
return 0;
}
if (n1 == null || n2 == null) {
return -1;
}
return n1.compareTo(n2);
}
Added
Note that this implementation also a common mistake as I am allowing compare(null,not_null) to equal compare(not_null,null) which also violates the contract - please use #Eran's solution or something like this.
public int compare(FileItem o1, FileItem o2) {
// null == null
if (o1 == null && o2 == null) {
return 0;
}
// null != not null
if (o1 == null || o2 == null) {
// Swap these around if you want 'null' at the other end.
return o1 == null ? -1: 1;
}
// Neither can be null now so this is safe.
String n1 = o1.getFileName();
String n2 = o2.getFileName();
// Same logic again.
if (n1 == null && n2 == null) {
return 0;
}
if (n1 == null || n2 == null) {
// Swap these around if you want 'null' at the other end.
return n1 == null ? -1: 1;
}
return n1.compareTo(n2);
}