Reputation: 813
So first, I have an object called news article that has three properties that I need to sort by:
Year (int), Month (int), type (String - online, paper)
So for example, it would be like this:
What is going on is that the month and year appear to be sorting correctly, but I'm having problems sorting by type. What's the proper way to sort by String in a compareTo?
Results at the moment:
Here's my method (I apologize for it being somewhat quirky, I've been trying different ways to sort by the type and was experimenting):
@Override
public int compareTo(Article rs) {
Integer x = 0;
Integer year1 = 0;
Integer year2 = 0;
Integer month1 = 99999;
Integer month2 = 99999;
Integer type1 = 99999;
Integer type2 = 99999;
if(rs.year != null && year != null) {
if(!rs.year.equals(""))
year1 = Integer.parseInt(rs.year);
if(!year.equals(""))
year2 = Integer.parseInt(year);
}
if(rs.month != null && month != null) {
if(!rs.month.equals(""))
month1 = Integer.parseInt(rs.month);
if(!month.equals(""))
month2 = Integer.parseInt(month);
}
if(rs.type == null)
type1 = 99999;
else
type1 = 0;
if(type == null)
type2 = 99999;
else
type2 = 0;
x = type2.compareTo(type1);
if(x != 0) {
return x;
}
x = year1.compareTo(year2);
if(x != 0) {
return x;
}
x = month1.compareTo(month2);
if(x != 0) {
return x;
}
return x;
}
Upvotes: 1
Views: 1228
Reputation: 83517
To answer your question, you can simply use compareTo()
to compare two String
objects. You just have to make sure that you compare them in the order that you want. Doing
x = type2.compareTo(type1);
compares the String
s in descending order. If you want to sort in ascending order, you need to do
x = type1.compareTo(type2);
In adition, I am curious about all the == null
or != null
checks. Since this is your own class, you should control whether or not the member fields can be null
. If I were writing a similar class, I would require that all fields are initialized by the constructor so that I don't need to check for null
values. This can greatly simplify all the other methods in the class, including this compareTo()
method.
Also, you should prefer built-in types over wrapper classes. In other words, use int
s for the year and month fields rather than Integer
s. This can also help simplify your code in a couple of ways. First, you won't have to worry about null
values. Second, you can compare to int
s with a simple subtraction:
int compareYears = this.year - rs.year;
if (compareYears != 0) {
return compareYears;
}
With these suggestions, you can not only fix the current problem, but you can also reduce the number of lines of code by at least half.
(Note that in general, you must be careful about comparing int
s using subtraction due to overflow. In this case, since we are comparing years, neither value should be negative, so there shouldn't be a problem. Of course, the rest of your class should enforce that the year
field is in fact a valid year.)
Upvotes: 0
Reputation: 691625
If the type is not null, you replace it by 0, whatever its value is. So comparing "paper" with "online" leads to comparing 0 with 0, which is obviously wrong.
My first advice would be to use proper types instead of String for everything. month and year should be int
s, and type should be an enum. You should also strive to make them non-nullable.
Once done, the comparison method would reduce to
public int compareTo(Article other) {
int result = this.type.compareTo(other.type);
if (result == 0) {
result = Integer.compare(this.year, other.year);
}
if (result == 0) {
result = Integer.compare(this.month, other.month);
}
return result;
}
Note that using an enum allows you to dictate specify the way types compare, simply by listing the values in the order you want.
Or even better, with Guava:
public int compareTo(Article other) {
return ComparisonChain.start()
.compare(this.type, other.type)
.compare(this.year, other.year)
.compare(this.month, other.month)
.result();
}
If the values are nullable, the above code would have to be changed to
public int compareTo(Article other) {
return ComparisonChain.start()
.compare(this.type, other.type, Ordering.natural().nullsLast())
.compare(this.year, other.year, Ordering.natural().nullsLast())
.compare(this.month, other.month, Ordering.natural().nullsLast())
.result();
}
Upvotes: 1
Reputation: 14617
I would refactor (eg throw away) your complete code and replace it by using CompareToBuilder.
This will create the following code:
enum ArticleType {
ONLINE, PAPER
}
class Article implements Comparable<Article> {
int year;
int month;
ArticleType type;
Article(int year, int month, ArticleType type) {
this.year = year;
this.type = type;
this.month = month;
}
@Override
public int compareTo(Article o) {
return new CompareToBuilder()
.append(this.type, o.type)
.append(this.year, o.year)
.append(this.month, o.month)
.toComparison();
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("month", month)
.add("year", year)
.add("type", type)
.toString();
}
}
@Test
public void testSortArticles() throws Exception {
List<Article> articleList = new ArrayList<>();
articleList.add(new Article(2012, 1, ArticleType.ONLINE));
articleList.add(new Article(2011, 1, ArticleType.ONLINE));
articleList.add(new Article(2011, 6, ArticleType.ONLINE));
articleList.add(new Article(2010, 1, ArticleType.ONLINE));
articleList.add(new Article(2010, 1, ArticleType.PAPER));
articleList.add(new Article(2010, 2, ArticleType.PAPER));
articleList.add(new Article(2010, 3, ArticleType.PAPER));
articleList.add(new Article(2012, 1, ArticleType.PAPER));
articleList.add(new Article(2012, 9, ArticleType.PAPER));
Collections.sort(articleList);
System.out.println(articleList);
}
Printing this will lead to the following:
[Article{month=1, year=2010, type=ONLINE}, Article{month=1, year=2010, type=PAPER},
Article{month=2, year=2010, type=PAPER}, Article{month=3, year=2010, type=PAPER},
Article{month=1, year=2011, type=ONLINE}, Article{month=6, year=2011, type=ONLINE},
Article{month=1, year=2012, type=ONLINE}, Article{month=1, year=2012, type=PAPER},
Article{month=9, year=2012, type=PAPER}]
Will provides a nicely sorted list. The offline/online is sorted too using an Enum (ArticleType). In my opinion, this looks a bit better than your current implementation.
Upvotes: 3