Say I have two comparators, a primary and a secondary. How can I sort an array first by the primary comparator, then by the secondary?
Say each object has a name and a number field.
Like
Bob 1
Bob 2
Jack 1
Jack 2
Is it possible without creating a new comparator?
Yes, you can accomplish your sort without creating a new comparator.
There is a well-known trick for sorting by a primary field, secondary, tertiary, etc: First sort by the least important field (tertiary), then the next important field (secondary), and finally the most important field (primary). But the sorting algorithm needs to be stable for this to work.
If you are sorting an array, use Arrays.sort(). If you are sorting a List, use Collections.sort(). Both of these methods are guaranteed to be stable.
Suppose your primary comparator object is stored in the variable primaryComp, and your secondary is in secondaryComp. Then here is some code to accomplish what you want:
Arrays.sort(mylist, secondaryComp); // This must come first!
Arrays.sort(mylist, primaryComp);
assuming your class is
class X {
String name;
int num;
}
then sorting will be
Arrays.sort(x, new Comparator<X>() {
#Override
public int compare(X o1, X o2) {
if (o1.name.equals(o2.name)) {
return Integer.compare(o1.num, o2.num);
}
return o1.name.compareTo(o2.name);
}});
Compare the second comparator first, and then the first comparator. I believe that should do the trick. You can create a class to do so.
class FullName {
public String firstName;
public String secondName;
}
Say you create a new name, called BobBobbins, assign values and then simple compare the second name first, and then the first name. You can have a static function to do the comparing:
public static bool compareTo ( FullName name1, FullName name2 ) {
// Algorithm here
}
Should you use a static comparator, you can will have to do this : FullName.compareTo( BobBobbins, CharlieChaplin );
Related
I am learning about arrays, and basically I have an array that collects a last name, first name, and score.
I need to write a compareTo method that will compare the last name and then the first name so the list could be sorted alphabetically starting with the last names, and then if two people have the same last name then it will sort the first name.
I'm confused, because all of the information in my book is comparing numbers, not objects and Strings.
Here is what I have coded so far. I know it's wrong but it at least explains what I think I'm doing:
public int compare(Object obj) // creating a method to compare
{
Student s = (Student) obj; // creating a student object
// I guess here I'm telling it to compare the last names?
int studentCompare = this.lastName.compareTo(s.getLastName());
if (studentCompare != 0)
return studentCompare;
else
{
if (this.getLastName() < s.getLastName())
return - 1;
if (this.getLastName() > s.getLastName())
return 1;
}
return 0;
}
I know the < and > symbols are wrong, but like I said my book only shows you how to use the compareTo.
This is the right way to compare strings:
int studentCompare = this.lastName.compareTo(s.getLastName());
This won't even compile:
if (this.getLastName() < s.getLastName())
Use
if (this.getLastName().compareTo(s.getLastName()) < 0) instead.
So to compare fist/last name order you need:
int d = getFirstName().compareTo(s.getFirstName());
if (d == 0)
d = getLastName().compareTo(s.getLastName());
return d;
The compareTo method is described as follows:
Compares this object with the specified object for order. Returns a
negative integer, zero, or a positive integer as this object is less
than, equal to, or greater than the specified object.
Let's say we would like to compare Jedis by their age:
class Jedi implements Comparable<Jedi> {
private final String name;
private final int age;
//...
}
Then if our Jedi is older than the provided one, you must return a positive, if they are the same age, you return 0, and if our Jedi is younger you return a negative.
public int compareTo(Jedi jedi){
return this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
By implementing the compareTo method (coming from the Comparable interface) your are defining what is called a natural order. All sorting methods in JDK will use this ordering by default.
There are ocassions in which you may want to base your comparision in other objects, and not on a primitive type. For instance, copare Jedis based on their names. In this case, if the objects being compared already implement Comparable then you can do the comparison using its compareTo method.
public int compareTo(Jedi jedi){
return this.name.compareTo(jedi.getName());
}
It would be simpler in this case.
Now, if you inted to use both name and age as the comparison criteria then you have to decide your oder of comparison, what has precedence. For instance, if two Jedis are named the same, then you can use their age to decide which goes first and which goes second.
public int compareTo(Jedi jedi){
int result = this.name.compareTo(jedi.getName());
if(result == 0){
result = this.age > jedi.age ? 1 : this.age < jedi.age ? -1 : 0;
}
return result;
}
If you had an array of Jedis
Jedi[] jediAcademy = {new Jedi("Obiwan",80), new Jedi("Anakin", 30), ..}
All you have to do is to ask to the class java.util.Arrays to use its sort method.
Arrays.sort(jediAcademy);
This Arrays.sort method will use your compareTo method to sort the objects one by one.
Listen to #milkplusvellocet, I'd recommend you to implement the Comparable interface to your class as well.
Just contributing to the answers of others:
String.compareTo() will tell you how different a string is from another.
e.g. System.out.println( "Test".compareTo("Tesu") ); will print -1
and System.out.println( "Test".compareTo("Tesa") ); will print 19
and nerdy and geeky one-line solution to this task would be:
return this.lastName.equals(s.getLastName()) ? this.lastName.compareTo(s.getLastName()) : this.firstName.compareTo(s.getFirstName());
Explanation:
this.lastName.equals(s.getLastName()) checks whether lastnames are the same or not
this.lastName.compareTo(s.getLastName()) if yes, then returns comparison of last name.
this.firstName.compareTo(s.getFirstName()) if not, returns the comparison of first name.
You're almost all the way there.
Your first few lines, comparing the last name, are right on track. The compareTo() method on string will return a negative number for a string in alphabetical order before, and a positive number for one in alphabetical order after.
Now, you just need to do the same thing for your first name and score.
In other words, if Last Name 1 == Last Name 2, go on a check your first name next. If the first name is the same, check your score next. (Think about nesting your if/then blocks.)
Consider using the Comparator interface described here which uses generics so you can avoid casting Object to Student.
As Eugene Retunsky said, your first part is the correct way to compare Strings. Also if the lastNames are equal I think you meant to compare firstNames, in which case just use compareTo in the same way.
if (s.compareTo(t) > 0) will compare string s to string t and return the int value you want.
public int Compare(Object obj) // creating a method to compare {
Student s = (Student) obj; //creating a student object
// compare last names
return this.lastName.compareTo(s.getLastName());
}
Now just test for a positive negative return from the method as you would have normally.
Cheers
A String is an object in Java.
you could compare like so,
if(this.lastName.compareTo(s.getLastName() == 0)//last names are the same
I wouldn't have an Object type parameter, no point in casting it to Student if we know it will always be type Student.
As for an explanation, "result == 0" will only occur when the last names are identical, at which point we compare the first names and return that value instead.
public int Compare(Object obj)
{
Student student = (Student) obj;
int result = this.getLastName().compareTo( student.getLastName() );
if ( result == 0 )
{
result = this.getFirstName().compareTo( student.getFirstName() );
}
return result;
}
If you using compare To method of the Comparable interface in any class.
This can be used to arrange the string in Lexicographically.
public class Student() implements Comparable<Student>{
public int compareTo(Object obj){
if(this==obj){
return 0;
}
if(obj!=null){
String objName = ((Student)obj).getName();
return this.name.comapreTo.(objName);
}
}
I'm new to Java and try to learn Java collections and try to sort arraylist with comparable interface. I follow some tutorials and I'm unable to understand what is happen within the compareto() method here. this is my code.
Student.java
package arraylistexample;
public class Student implements Comparable<Student>{
private String studentName;
private int age;
private int rollno;
public Student(String studentName, int age, int rollno){
this.studentName=studentName;
this.age=age;
this.rollno=rollno;
}
public String getStudent(){
return studentName;
}
public int getAge(){
return age;
}
public int getRollno(){
return rollno;
}
public void setStudent(String Student){
studentName=Student;
}
public void setAge(int age){
this.age=age;
}
public void setRollno(int rollno){
this.rollno=rollno;
}
public int compareTo(Student compares) {
int compareage=((Student)compares).getAge();
/* For Ascending order*/
return this.age-compareage;
}
public String toString() {
return "[ rollno=" + rollno + ", name=" + studentName + ", age=" + age + "]";
}
}
ArrayListSorting.java
package arraylistexample;
import java.util.*;
public class ArrayListSorting {
public static void main(String[] args){
ArrayList<Student> obj=new ArrayList<Student>();
obj.add(new Student("Peter", 27,1));
obj.add(new Student("John",26,7));
obj.add(new Student("Jack",21,5));
Collections.sort(obj);
for(Student str:obj){
System.out.println(str);
}
}
}
The problem is I can't understand how caompareto() method works in here. I googled and read many tutorials. But didn't get clear idea. Can anyone help me.
How compareTo works
If the two elements (a,b) being compared are already in the right order, a.compareTo(b) will return a value that is <= 0, so nothing has to happen.
If they aren't in the right order, the return value is > 0, indicating that they must be interchanged.
So in your case student object passed in your compareTo method whose age is greater than your reference object (this) student age they get interchanged to default sorting which is ascending.
Writing a compareTo method for your class lets you specify what criteria your program will use to decide which of two objects of that class should come first in order.
If you don't write a compareTo method for your class, then your program has no way of knowing which order to put two objects in - and therefore it has no way of sorting a whole lot of objects.
But if you write a compareTo method in your class, AND indicate that your class implements the Comparable interface, then your program will be able to sort any number of objects of that class.
What that means is that you have to decide what order you want your Student objects to appear in. Maybe you want them sorted by roll number. So you write your compareTo accordingly, like this.
public int compareTo(Student other) {
return rollno - other.rollno;
}
This particular method will return
a positive number if the current student has a higher roll number than the student called other,
a negative number if the current student has a lower roll number than the student called other.
zero if you try to compare a student to itself.
So it meets all the criteria that a compareTo method has to meet; and it can be used to sort a bunch of students. The actual algorithm that's used for the sort is buried in the Collections.sort method. You don't need to know what it is - you only need to know that it uses your compareTo method in the course of doing the sort.
From Oracle docs of the compareTo method:
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
When you implement the method for your own composite-object, you are creating a way to compare those objects.
For example:
A Student named "Peter" and a 'Student named "Greg" - who is bigger/smaller?
That's up to you to decide... You can either choose alphabetical order of name, or ages, or any other component/member/logic to decide.
Edit:
As mentioned by Eran in the comments, the way Collections.sort works is using the compareTo method. From the docs:
orts the specified list into ascending order, according to the natural ordering of its elements. All elements in the list must implement the Comparable interface. Furthermore, all elements in the list must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the list).
For sorting collections a compareTo function is used to see if object1 is bigger or smaller than object2 by doing
object1.compareTo(object2)
According to documentation of Intger Object's compareTo
the value 0 if this Integer is equal to the argument Integer; a value
less than 0 if this Integer is numerically less than the argument
Integer; and a value greater than 0 if this Integer is numerically
greater than the argument Integer (signed comparison).
You can find a similar documentation for String Object. This same practice is used for all objects generally.
So, the idea is that the compareTo method in your class should basically return
<0 if object1 < object2
=0 if object1 = object2
>0 if object1 > object2
Using this the Collections API can sort objects.
So, in your case you can see
public int compareTo(Student compares) {
int compareage=((Student)compares).getAge();
/* For Ascending order*/
return this.age-compareage;
}
is used where it is allowing comparison of students based on their age.
I'm using a HashMap in which I use an ArrayList as a value.
Like this:
Map<Movie, List<Grades>> gradedMovies = new HashMap();
I'm trying to create a method with which I could iterate through the values to see if a key(movie) already exists. If it does, I would like to add a new value(grade) into the list that is assigned to the particular key(movie). Something like this:
public void addGrade(Movie movie, Grade grade) {
// stuff here }
Ultimately I wan't to be able to print a Map which would display the Movies and its' grades after they've been added to the map.
How is this accomplished? Or is my whole approach (using a Map) totally wrong?
Thanks for any assistance. (This is homework)
I think you're on the right path, just make sure your movie object implements equals and hashCode so it can work as a true key for the hash map.
If you want pretty printing just implement the toString method.
public void addGrade(Movie movie, Grade grade) {
if (!gradedMovies.containsKey(movie)) {
gradedMovies.put(movie, new ArrayList());
}
gradedMovies.get(movie).add(grade);
}
hope this helps, Cheers!
You can use something like that:
public void addGrade(Movie movie, Grade grade) {
if (!gradedMovies.containsKey(movie)) {
gradedMovies.put(movie, new ArrayList<Grade>());
}
gradedMovies.get(movie).add(grade);
}
You need override the method equals
I don't know why you're looking for an index particularly - the point of a Map is that you can look up entries by their keys.
So as a starting point, the first line of your addGrade method could look like
List<grades> grades = gradedMovies.get(movie);
and you can hopefully take it from there. (Remember to look at the documentation to see what happens if the map doesn't contain the given movie yet...)
I could iterate through the values to see if a key(movie) already exists
You don't need to iterate through the map, just call gradedMovies.containsKey( movieToCheck ).
Note that when using Movie as a key you should provide a sensible implementation of equals() and hashCode().
You're doing ok! but you should consider a couple of things:
While finding a value on the map, your Movie object has to override equals and hashChode. Java will always use the equals method on comparations, mainly when it comes to automatic ones (like verifying if a list contains an item or, in this case, if a key value is equal to a given one). Remember that equals defines the uniquity of an item, so you should make a comparation based on a particulary unique attribute, like an identification number or (for this case) it's name.
To print the map, iterate over the keySet, either manually (enhanced "for" loop) or with an iterator (which can be obtained directly through the .iterator() method). For each movie, you print the list of grades in a similar fashion.
I don't know if you're familiar with String printing, but some special combination of characters can be added to a String to give it some sort of formatting. For example:
\n will insert a line break
\t is a tabulation
Hope this helps to erase some doubts. Good luck!.
Check out Guava's Multimap. That is exactly what it does.
private Multimap<Movie, Grade> map = ArrayListMultimap.create();
public void addGrade(Movie movie, Grade grade){
map.put(movie, grade);
}
It will take care of creating the list for you.
public void addGrade(Movie movie, Grade grade) {
boolean found = false;
for(Movie m : gradedMovies.keyset()) {
// compare the movies
if(/* match on movies */) {
gradedMovies.get(m).add(grade);
found = true;
}
}
if(!found) {
gradedMovies.put(movie, new ArrayList().add(grade));
}
}
gradedMovies.containsKey(movie);
for(Map.Entry<Movie,List<Grades>> entry : gradedMovies.entrySet()){
Movie key = entry.getKey();
}
Is it possible to have an array that contains two different types of data? I want to have an array that contains a double and also a string. I attempted:
ArrayList<double><String> array;
But that didn't work.
Sorry for the silly question, but it has been a while since I have used something like this.. Can you refresh my memory on how would I declare and populate such an array?
And then to take it a step further, I would like to sort the array by the double if possible?
Thanks!
Firstly, it's worth being clear about the difference between an array and an ArrayList - they're not the same thing at all.
However, in either case you can't do what you want. The closest you can probably come is declaring your own type. (EDIT: My original code had a double or a string... I've now changed it to be a double and a string. Let me know if this change isn't what you had in mind.)
public final class DoubleAndString
{
private final String stringValue;
private final double doubleValue;
public DoubleAndString(String stringValue, double doubleValue)
{
this.stringValue = stringValue;
this.doubleValue = doubleValue;
}
public String getString()
{
return stringValue;
}
public String getDouble()
{
return doubleValue;
}
}
Then create an ArrayList<DoubleAndString> or a DoubleAndString[].
Now, this feels somewhat vanilla at the moment - presumably the double and string values actually have a greater meaning - a name and a score, for example. If so, encapsulate that in a type which describes the pairing more appropriately.
As for ordering - you could make DoubleAndString implement Comparable<DoubleAndString> - but unless that's the only natural ordering which makes sense, I'd write a Comparator<DoubleAndString>:
public class DoubleComparator implements Comparator<DoubleAndString>
{
public int compare(DoubleAndString ds1, DoubleAndString ds2)
{
return Double.compare(ds1.getDouble(), ds2.getDouble());
}
}
Then you can use Collections.sort to sort an ArrayList<DoubleAndString> or Arrays.sort to sort an array.
You can use ArrayList<Object> and you can then use anything you'd like. Encapsulate the double in a Double object and when you retrieve the object use instanceof to check if it's really a double or a String.
I must say, it's unlikely this 'design' would win you any awards. Is it possible to rethink the solution you're considering for your problem, and see if you could do with a different kind of approach?
It sounds like you want a Map. Since you wish to sort the Map, a TreeMap may be optimal.
Map<Double, String> myMap = new TreeMap<Double, String>();
Maps are associative. Each double has an associated string. If you want multiple strings per double, you can use a
Map<Double, ArrayList<String>>
You might already know this, but it is not certainly not a good idea to store different types in a list. By definition an array is a collection of similar objects and stuffing all kinds in it makes things fuzzy. So really you would rather have a separate type to hold these different values.
Well, if you want to have an array with an arbitrary number of elements, then you simply need to use a type that is a common ancestor to both. In this case, that would be Object (since String and Double both inherit from Object). This will require you to check the types, though, when you retrieve or use them.
If you are using a fixed number of multiple different types, then what you really want is a "tuple". However, Java currently does not have an implementation of tuple available. For two items:
public class Pair<T1,T2>
{
public Pair(){
this(null,null);
}
public Pair(T1 x1){
this(x1,null);
}
public Pair(T1 x1, T2 x2){
_x1 = x1;
_x2 = x2;
}
public T1 getFirst(){
return _x1;
}
public T1 getSecond(){
return _x2;
}
private T1 _x1;
private T2 _x2;
}
You can just do ArrayList<object> arraylist and then you can put anything in it, but that may not be what you want.
Then, to sort you would just use your own comparator but, as theatrus mentioned, are these two values supposed to be connected, or do you have a single-dimension array with two different data types?
An ArrayList by definition only contains one object per position. You could do something like this:
List<MyTuple> list = new ArrayList<MyTuple>();
public static class MyTuple implements Comparable<MyTuple> {
private Double doubleValue;
private String stringValue;
//getters and setters
public int compareTo(MyTuple tuple) {
return doubleValue.compareTo(tuple.getDoubleValue());
}
}
You can then use the Collections.sort() method to sort it by the Doubles.
What do you want to do?
If it is not a key value mapping, you should create a new class for this.
You may want to look at the Number base class.
List<Number> list = new ArrayList<Number>();
list.add(new Integer(3));
list.add(new Double(5.2));
You may interpret the numbers as strings, using NumberFormat:
NumberFormat formatter = new DecimalFormat("#.##");
String s = formatter.format(list.get(0));
Though this may not be what you want, you are a bit short on details about your end goal.
if you are basically not trying to do any comparisons/sorting on the ArrayList then you could create something as below:
List list = new ArrayList();
otherwise.. Jon Skeet's answer was best approach.
I am building a class that has a mapping of strings to integers. So if I have 3 apples I would have a mapping of apples to 3.
I need to write a class that sorts the name of the objects by decreasing numbers.
So if I have
(apples, 3)
(oranges, 2)
(bananas, 5)
I will get
(bananas, 5), (apples, 3), (oranges 2)
I was wondering if there's already a class out there that would make my life easier or how I would implement this.
Thanks.
You should be able to put your objects (apples, 3) (oranges, 2) (bananas, 5) into a List and then call Collections.sort(yourlist). You'd then want to make sure the object you declared implements the Comparable interface.
More information is available at http://java.sun.com/docs/books/tutorial/collections/interfaces/order.html
Let's say you declared you object as
public class FruitAndCount implements Comparable<FruitAndCount> {
private final String name;
private final Integer count;
public FruitAndCount(String name, int count) {
this.name = name;
this.count = count;
}
public String name() { return name; }
public int count() { return count; }
public int compareTo(FruitAndCount o) {
return this.count.compareTo(o.count);
}
}
You should then be able to make the following call which will sort your list:
FruitAndCount fruitArray[] = {
new FruitAndCount("Apples", 3),
new FruitAndCount("Oranges", 2),
new FruitAndCount("Bananas", 5)
};
List<FruitAndCount> fruit = Arrays.asList(fruitArray);
Collections.sort(fruit);
You should then have a sorted list of fruit.
It's always nice to be able to make a class implement Comparable, but sometimes you can't, or it is undesirable (for instance, if you need to be able to compare the same type in different ways, based on different attributes).
In this case, it is advisable to use the overloaded Collections.sort() method, which takes a List<T> to sort and a Comparator<T> to determine how the objects should be sorted. This is much cleaner than making new tuples out of your old tuples, and can be more flexible than implementing Comparable (which is also a valid solution).
You really want to take a look at TreeMap.
Assuming the counts are unique, you simply reverse the tuples, storing the count as the key and the name of the fruit as the value. TreeMap then stores the items sorted in ascending order by the key value, and you can read the values back. Since the sorting is done on the insertion the retrieval time is very low.
If you have non-unique counts there's an easy solution here that will let you take advantage of TreeMap.