Java 6 uses merge sort to compare two objects in Collections.sort() whereas Java 1.7 uses Timsort
I have this class for object to sort
Class ObjectSort
{
String Name = "";
int priority = 0;
public ObjectSort (String name, int priority)
{
this.Name = Name;
this.priority = priority;
}
public getPriority()
{
return priority;
}
}
and my test class is
TestClass
{
...main()
{
List<ObjectSort> sorted = new ArrayList<ObjectSort> ();
sorted.add ("Table", 99);
sorted.add ("Chair", 1);
Collections.sort(sorted, new Comparator ());
}
// inner class to define comparator logic
private static final class Comparator implements java.util.Comparator<ObjectSort>
{
#Override
public int compare (ObjectSort f1, ObjectSort f2)
{
try
{
// Get the allocation priorities
int priority1 = f1.getPriority ();
int priority2 = f2.getPriority ();
if (priority1 == priority2)
return 0;
else
return (priority1 > priority2 ? 1 : 0);
}
catch (Exception e)
{
// Shouldn't happen, because we have the objects OK and there's no database activity
// happening here.
assert true;
}
return 0;
}
}
}
Now when we run the code in java 1.6, it sorts it correctly, Chair comes BEFORE table that is it is sorting it is ASCENDING order, which I want.
But where the code is run in Java 1.7, it does not sort it at all, Table comes before Chair. I checked and 1.6 uses merge sort whereas 1.7 is using Timsort. Please help me tell what is wrong in my code ?
UPDATE
In 1.7 in variable f1, Chair comes during code execution whereas in 1.6 Table comes!
Thanks!
Aiden
The problem is that your comparator is broken. When you have a Comparator
comparator.compare(a, b) == -comparator.compare(b, a)
The reason Java 7 doesn't accept this is; Java 7 has more checks that this condition is true.
they have updated the java to 1.7 :( and this code is not working there now :(
It never worked, it probably didn't sort properly before, but you didn't get a runtime error before.
A shorter version which will work is; (Don't reuse the name of common built in classes)
static class ObjectSortComparator implements Comparator<ObjectSort> {
#Override
public int compare (ObjectSort f1, ObjectSort f2) {
// Get the allocation priorities
int priority1 = f1.getPriority ();
int priority2 = f2.getPriority ();
return priority1 == priority2 ? 0 : (priority1 > priority2 ? 1 : -1);
}
}
Note: in Java 8 you don't need to write this yourself, you can do
sorted.sort(Comparator.comparingInt(ObjectSort::getPriority));
Related
I have implemented a TreeMap that contains blueprints (to simplify it).
private TreeMap<BuildingFloorKey, Blueprint> blueprints = new TreeMap<>((o1, o2) -> {
int value = o1.compareTo(o2);
return value;
});
To use building (in my case called complex) and floor as a tuple key, I wrote the following class:
public static class BuildingFloorKey {
private Complex mComplex;
private int mFloor;
public BuildingFloorKey(Complex complex, int floor){
mComplex = complex;
mFloor = floor;
}
#Override
public boolean equals(Object other) {
if (!(other instanceof BuildingFloorKey)) return false;
BuildingFloorKey that = (BuildingFloorKey) other;
return mFloor == that.mFloor && mComplex.equals(that.mComplex);
}
#Override
public int hashCode() {
return Arrays.hashCode(new Object[]{mComplex, mFloor});
}
public int compareTo(BuildingFloorKey otherKey){
if(this.equals(otherKey)) return 0;
//same complex -> compare floors
else if (this.getComplex().equals(otherKey.getComplex())){
return otherKey.getFloorInt() - this.getFloorInt();
}
//different complexes (incl. some modification for special cases)
else return -(Math.abs(otherKey.mFloor + 2) + 100);
}
}
I am working on an Android App and I want to click through the blueprints via buttons. For that I make use of the methods TreeMap.lowerKey(otherKey) and TreeMap.higherKey(otherKey). Like so:
#Override
public void onNextPlanClicked() {
nextFloorPlan = blueprints.higherKey(currentlyDisplayedPlan);
drawFloorPlan(nextFloorPlan);
}
As an example, I have a usecase where the set of blueprints is
04|02
03|03
04|-1
03|00
(format: complex|floor). Unfortunately, it is not sorted properly in the TreeMap (as you can see - the list above is sorted like the entries of the TreeMap in the debugger).
I read something about TreeMap Sorting using case-sensitive Strings. But I'm actually using integers. So I don't get why sorting and using lowerKey() and higherKey() not working properly. Did I mess up with the comparator? Can someone help please?
I think your issues is a very simple one, your compareTo method should have an override. You need to add implements Comparable to your BuildingFloorKey definition, which will then take your compareTo argument as a comparable that TreeMap can recognize.
I have an ArrayList of object called Course and I'm trying to sort it in 2 ways, by courseID and courseStartTime.
Edit: to clarify I mean I want to sort it by courseID at some point in time, and at another time later sort it by courseStartTime.
class Course implements Comparable<Course> {
private int courseID;
private String courseBeginTime;
#Override
public int compareTo(Course course) {
//what to return?
}
If I wrote 2 of my own comparators, one to compare courseID and the other for courseStarTime, then the compareTo() method in the class isn't used and I don't know what to return.
If I want to use the compareTo() method, I'm not sure how to write it so I can compare courseID and courseStartTime.
You can implement two different comparators.
public class CourseComparatorById implements Comparator<Course> {
#Override
public int compare(Course o1, Course o2) {
// for example - sort ascending by ID
return o1.getId() - o2.getId();
}
}
public class CourseComparatorByStartTime implements Comparator<Course> {
#Override
public int compare(Course o1, Course o2) {
// for example - sort ascending by start time
return o1.getStartTime() - o2.getStartTime();
}
}
And then use them to sort the array.
List<Course> courses = ...
Collections.sort(courses, new CourseComparatorById());
// now it's sorted by ID
Collections.sort(courses, new CourseComparatorByStartTime());
// now it's sorted by start time
You can also try the Java 8 Lambda way:
// this sorts by courseID
courseList.sort((c1, c2) -> Integer.valueOf(c1.courseID).compareTo(c2.courseID));
// this sorts by String courseBeginTime
courseList.sort((c1, c2) -> c1.courseBeginTime.compareTo(c2.courseBeginTime));
Note that is Java 8 you don't have to use Collections.sort, because the new List interface also provides a sort method
I have a feeling that this is being used for an online registration web app ...
you will probably be fetching the data source from a RDB ... It wouldnt be wise to put ALL courses in one list (one entity) and save that. I would create an object (containing courseID and courseBeginTime) for EVERY course and save them all. Then when querying, add hints to sort your entities based on whatever root parameters you have in them (like courseID or courseBeginTime), ending with a List containing objects sorted the way you want :) :)
May be you should do something like this
public class Course implements Comparator<Course> {
private int compareTime(int lhsTime, int rhsTime) {
if (lhsTime > rhsTime) {
return 1;
} else if (lhsTime == rhsTime) {
return 0;
} else {
return -1;
}
}
#Override
public int compare(Course lhs, Course rhs) {
if (lhs.id > rhs.id) {
return 1;
//Get the time object from course obj and pass to compaerTime
} else if (lhs.courseStarTime == rhs.courseStarTime) {
return compareTime(lhs, rhs);
} else {
return -1;
}
}
}
With the use of below code, I am finding out which datacenter I am in and it is working fine..
public enum DatacenterEnum {
DEV, DC1, DC2, DC3;
private static DatacenterEnum compareLocation() {
String ourhost = getHostName();
for (DatacenterEnum dc : values()) {
String namepart = "." + dc.name().toLowerCase() + ".";
if (ourhost.indexOf(namepart) >= 0) {
return dc;
}
}
return null;// I don't want to do this now.
}
}
But it might be possible that it is not able to find any datacenter, so currently I am returning null.
Is there any direct way or a single line command by which I can return randomly either DC1 or DC2 or DC3 in the ENUM instead of returning null?
I know one way is to make a list of string and then randomnly select any integer between 0 to 2 inclusive and then find the string. But it is too much code, actually it's not but just trying to see is there any other way we can do this?
Any simple and direct way which I can use in the ENUM directly?
Here's the one line:
return DataCenterEnum.values()[new Random().nextInt(3) + 1)];
For those who require tighter control on their code, here's a safer version, which does not depend on the order of the enum instances:
return new DataCenterEnum[]{DC1, DC2, DC3}[new Random().nextInt(3)];
Here is a generic solution that will work for any enumeration.
Convenience method for single exclusion:
public static <E extends Enum<E>> E getRandom(Class<E> aEnum, E exclude) {
return getRandom(aEnum, Collections.singletonList(exclude));
}
Generic method that works with one or more exclusions:
public static <E extends Enum<E>> E getRandom(Class<E> aEnum, List<E> exclude){
//Convert set of enums into a list
List<E> enums = new ArrayList<E>(EnumSet.allOf(aEnum));
//Remove items from the list that you want to exclude
enums.removeAll(exclude);
int size = enums.size();
if(size != 0){
//Get random enum
int randomIndex = new Random().nextInt(size);
return enums.get(randomIndex);
} else {
throw new IllegalArgumentException("Empty Enumeration after excludes");
}
}
For your example you could call
EnumUtil.getRandom(DatacenterEnum.class, DatacenterEnum.DEV);
You could use the values() method, which returns an array. Then just use Math.random() to return a random instance.
Here is an example:
public static void main (String[] args) {
String[] arr = {"DEV","DC1","DC2","DC3"}; //returned by Enum.values(), you get the idea
String randElement = arr[(int) ((Math.random() * 3) +1)];
System.out.println(randElement);
}
Basically it boils down to generating a random number between 1 and n :)
I am trying to sort an arraylist by string length, i know of implementing Comparator, but i was wondering if this could be done within my function, without adding any extra classes or methods? Ideally I want to output them shortest to longest, but that I can do!
Here is a snippet of the method i would like to implement the comparator with.
public static void sCompare(BufferedReader r, PrintWriter w) throws IOException {
ArrayList<String> s= new ArrayList<String>();
String line;
int n = 0;
while ((line = r.readLine()) != null) {
s.add(line);
n++;
}
//Collections.sort(s);
Iterator<String> i = s.iterator();
while (i.hasNext()) {
w.println(i.next());
}
}
Thanks in advance for any input!
I don't see anything wrong with implementing the Comparator interface.
If your only concern is doing everything in the function, you could use an anonymous implementation. Something along the lines of :
Collections.sort(s, new Comparator<String>() {
#Override
public int compare(String o1, String o2) {
return o1.length() - o2.length();
}
});
(that would replace you current line //Collections.sort(s);)
PS : you never use the value of n.
PPS: You may have to invert o1 and o2 depending of the order you want in the return statement.
Another example of implementing an interface with an anonymous class
I'm going to assume by "class" you mean "top level class", thus allowing the use of an anonymous class:
Collections.sort(s, new Comparator<String>() {
public int compare(String a, String b) {
// java 1.7:
return Integer.compare(a.length(), b.length());
// java 1.6
return a.length() - b.length();
}
});
I'm trying to use a priority queue in my code, and for some reason when I remove the objects, they aren't in order. Do you know what i"m doing wrong?
Here's my code:
the contructor:
recordedsong = new PriorityQueue<recordedNote>(50, new Comparator<recordedNote>()
{
public int compare(recordedNote n1, recordedNote n2)
{
long l = n1.rt()-n2.rt();
int i = (int)l;
return i;
}
});
where each recordedNotehas a long value that is returned my the method rt().
But when I call
while (!Song.isEmpty())
{
recordedNote temp = (recordedNote)Song.remove();
and then print temp.rt() for each one, all the numbers are out of order. And not just like reverse order, but all over the place, like 1103, 0, 500, 0, 220 orders like that.
Can you see if there's anything wrong with my contructor?
Thanks!
remove should work, and in fact it does work fine in a small example program that I created to help answer this question:
import java.util.Comparator;
import java.util.PriorityQueue;
public class TestPriorityQueue {
public static void main(String[] args) {
long[] noteTimes = {1103L, 0L, 500L, 0L, 220L, 1021212812012L};
PriorityQueue<RecordedNote> noteQueue = new PriorityQueue<RecordedNote>(10,
new Comparator<RecordedNote>() {
#Override
public int compare(RecordedNote o1, RecordedNote o2) {
Long time1 = o1.getTime();
Long time2 = o2.getTime();
// uses Long's built in compareTo method, so we
//don't have to worry as much about edge cases.
return time1.compareTo(time2);
}
});
for (int i = 0; i < noteTimes.length; i++) {
RecordedNote note = new RecordedNote(noteTimes[i]);
System.out.println(note);
noteQueue.add(note);
}
System.out.println();
while (noteQueue.size() > 0) {
System.out.println(noteQueue.remove());
}
}
}
class RecordedNote {
private long time;
public RecordedNote(long time) {
this.time = time;
}
public long getTime() {
return time;
}
#Override
public String toString() {
return "[Time: " + time + "]";
}
}
So this begs the question, why isn't it working for you? Myself, I don't see enough coherent code in your question to be able to answer this. We're not sure what is Song as I don't see this declared as a class or a variable, and I also don't see where you're using your PriorityQueue variable, recordedsong, anywhere. So I suggest you do the same thing as I: create a small compilable runnable program that we can run and modify and that demonstrates your problem, an http://sscce.org
I guess there is a possibility for i getting 0. So modify compare method so that it returns a positive value rather than the result.
Reading the API docs for PriorityQueue, it states the following:
The Iterator provided in method iterator() is not guaranteed to traverse the elements of the priority queue in any particular order. If you need ordered traversal, consider using Arrays.sort(pq.toArray()).
My guess is that remove() is not obligated to follow the natural ordering, either.