Reputation: 2988
I have a list of objects with a method getPriority() which returns the objects priority in a string and a method getName() which returns the objects name (also a string). I have a list, 'priority', which contains strings like 'high' 'medium' and 'low'.
I would like to order the list of objects by the position of the getPriority() method in the order list and for objects which have the same priority or no priority then order them alphabetically based on name.
For example:
List<MyObject> objects = new ArrayList<MyObject>();
objects.add(new MyObject("apple", "low"));
objects.add(new MyObject("bacon", "medium"));
objects.add(new MyObject("cheese", "low"));
objects.add(new MyObject("doughnut", "high"));
objects.add(new MyObject("eggs", null));
objects.add(new MyObject("fudge", null));
List<String> priority = new ArrayList<String>();
priority.add("high");
priority.add("medium");
priority.add("low");
would be sorted as: doughnut, bacon, apple, cheese, eggs, fudge.
It is important to not use an enum for priority as it needs to be customisable by the end user. It also needs to accept a null priority as priority is taken from a database and the objects still need to be sorted if not found in the database.
Upvotes: 2
Views: 855
Reputation: 3882
I would recommend using Guava Ordering.
Using Ordering.explicit
allows us to compare things in the order that they appear in a list.
In order to sort the objects by name after they are sorted by priority, the two orderings are composed together with Ordering.thenComparing
.
// Sorts MyObject by name
public static final Ordering<MyObject> MY_OBJECT_FOOD_ORDERING = new Ordering<MyObject>() {
@Override
public int compare(final MyObject left, final MyObject right) {
return ComparisonChain.start()
.compare(left == null ? null : left.getName(), right == null ? null : right.getName())
.result();
}
};
public static final Ordering<MyObject> buildPriorityOrdering(final List<String> priorities) {
return buildPriorityOrdering(Ordering.explicit(ImmutableList.<String> builder()
.addAll(priorities)
.build())
.nullsLast());
}
public static final Ordering<MyObject> buildPriorityOrdering(final Ordering<String> priorityOrdering) {
return new Ordering<ListOrdering.MyObject>() {
@Override
public int compare(final MyObject left, final MyObject right) {
return ComparisonChain.start()
.compare(left == null ? null : left.getPriority(), right == null ? null : right.getPriority(),
priorityOrdering)
.result();
}
};
}
public static void main(final String... args) {
final List<String> priorities = ImmutableList.<String> builder()
.add("high")
.add("medium")
.add("low")
.build();
final List<MyObject> myObjects = ImmutableList.<MyObject> builder()
.add(new MyObject("doughnut", "high"))
.add(new MyObject("cheese", "low"))
.add(new MyObject("eggs", null))
.add(new MyObject("apple", "low"))
.add(new MyObject("fudge", null))
.add(new MyObject("bacon", "medium"))
.build();
final List<MyObject> myObjectsSorted = FluentIterable.from(myObjects)
.toSortedList(buildPriorityOrdering(priorities).thenComparing(MY_OBJECT_FOOD_ORDERING));
/*
* Results:
* MyObject [name=doughnut, priority=high]
* MyObject [name=bacon, priority=medium]
* MyObject [name=apple, priority=low]
* MyObject [name=cheese, priority=low]
* MyObject [name=eggs, priority=null]
* MyObject [name=fudge, priority=null]
*/
System.out.println(Joiner.on("\n")
.join(myObjectsSorted));
}
public static class MyObject {
private final String name;
private final String priority;
public MyObject(final String name, final String priority) {
this.name = name;
this.priority = priority;
}
public String getName() {
return this.name;
}
public String getPriority() {
return this.priority;
}
@Override
public String toString() {
return "MyObject [name=" + this.name + ", priority=" + this.priority + "]";
}
}
Thanks @Barett for nullsFirst nullsLast
Upvotes: 1
Reputation: 5948
Add a new comparator class to sort your objects, defined as follows:
class MyObjectPrioritizer extends Comparator<MyObject> {
List<String> priority;
MyObjectPrioritizer(List<String> priorityOrder) {
priority = priorityOrder;
}
public int compare(MyObject o1, MyObject o2) {
int v1 = resolveValue(o1);
int v2 = resolveValue(o2);
return v1 - v2;
}
int resolveValue(MyObject o) {
int sort = priority.indexOf(o.priority));
// put null & unknown values at the end
if (sort == -1) sort = Integer.MAX_VALUE;
return sort;
}
}
Then just:
Collections.sort(objects, new MyObjectPrioritizer(priority));
Upvotes: 2
Reputation: 42010
You can use a comparator for sort the list. e.g.:
Code:
List<MyObject> objects = new ArrayList<MyObject>();
objects.add(new MyObject("apple", "low"));
objects.add(new MyObject("bacon", "medium"));
objects.add(new MyObject("cheese", "low"));
objects.add(new MyObject("doughnut", "high"));
objects.add(new MyObject("eggs", null));
objects.add(new MyObject("fudge", null));
final List<String> priority = new ArrayList<String>();
priority.add("high");
priority.add("medium");
priority.add("low");
Comparator<MyObject> comparator = new Comparator<MyObject>() {
public int compare(MyObject o1, MyObject o2) {
int p1 = priority.indexOf(o1.getPriority());
int p2 = priority.indexOf(o2.getPriority());
if (p1 == -1 && p2 != -1) {
return 1;
}
if (p1 != -1 && p2 == -1) {
return -1;
}
if (p1 != p2) {
return p1 - p2;
}
return o1.getName().compareTo(o2.getName());
}
};
Collections.sort(objects, comparator);
for(MyObject obj : objects) {
System.out.printf("%-10s %10s%n", obj.getName(), obj.getPriority());
}
Output:
doughnut high
bacon medium
apple low
cheese low
eggs null
fudge null
Upvotes: 1