问题
I'm looking to compute the highest ordinal enum value from a list of enum properties in a list of beans.
For example, I have:
@Data
public class MyBean {
private Priority priority;
}
and
public enum Priority {
URGENT("Urgent"),
HIGH("High"),
MEDIUM("Medium"),
LOW("Low");
@Getter
private final String value;
Priority(String value) {
this.value = value;
}
@Override
public String toString() {
return getValue();
}
}
If I have a List
of MyBeans
, how can I find the max ordinal value of the bean's priority
in the list?
Example:
{myBean1, myBean2, myBean3, myBean4} where
myBean1.getPriority() = Priority.LOW
myBean2.getPriority() = Priority.URGENT
myBean3.getPriority() = Priority.HIGH
myBean4.getPriority() = null
returns Priority.URGENT
I'm thinking the worst case is that I could iterate the values()
in the enum starting with Collections.min(Arrays.asList(Priority.values()));
and loop through each bean to see whether the value matches. But this seems tedious.
回答1:
I would give each priority a specific numeric value and add a method which can compare them. Unfortunately enums can't implement Comparable (for consistency) which is a bit of a bummer here.
The fact that you are returning null sentinel values complicates things slightly. I would rethink this if I were you. Consider a "Default" priority instead which can act as our priority if one is missing.
I've added the default priority as a totally unique option, but depending on what you want you could just use medium or low as the default.
public enum Priority {
URGENT ("Urgent", 10),
HIGH ("High", 5),
MEDIUM ("Medium", 2),
LOW ("Low", 0),
DEFAULT("Default",-1);
@Getter
private final String name;
private final int value;
Priority(String name, int value) {
this.name = name;
this.value = value;
}
@Override
public String toString() {
return getName();
}
public int compare(Priority that) {
return Integer.compare(this.value, that.value);
}
}
You will then need to change your priority getter to return this new default rather than null
, else we'll get null pointer exceptions later on.
public class MyBean {
private Priority priority;
// Never returns null :)
public Priority getPriority() {
return (priority != null) ? priority : Priority.DEFAULT;
}
}
Now we've done the "hard" work, getting the highest priority is super easy:
Priority max = Collections.max(beans, (a,b) ->
a.getPriority().compare(b.getPriority())
).getPriority();
回答2:
You can use the Stream API and especially the max(Comparator)
method to find the bean with the highest priority.
Note: your enum has the priorities in reverse ordinal order. In my example, I will assume the opposite to make the code more self-explanatory. Switch max
for min
and nullsFirst
with nullsLast
, if you do not re-arrange your enum.
Bean highest = beans.stream()
.max(Comparator.comparing(Bean::getPriority,
Comparator.nullsFirst(Comparator.comparing(Priority::ordinal))))
.orElse(null);
return highest.getPriority();
If you only need the priority, you can simplify:
return beans.stream()
.map(Bean::getPriority)
.filter(Objects::nonNull)
.max(Comparator.comparing(Enum::ordinal))
.orElse(null);
It works as follows:
- Look at every bean
- Take its priority
- Discard null priorities
- Pick the maximum priority
- Beware of the list being empty or all priorities being null
Note: My examples are very verbose. shmosel's (deleted) answer shows that you can order enums naturally by ordinal
, making for nicer comparators. Comparator.comparing(Enum::ordinal)
can just become Comparator.naturalOrder()
.
回答3:
Another safe way to do it would be to use Ordering
from guava as a way to define an explicit
order, without relying on the Enum ordinal
that can change as others have pointed out.
// creates an explicit Order (from left to right)
Ordering<Priority> order = Ordering.explicit(Priority.LOW,
Priority.MEDIUM,
Priority.HIGH,
Priority.URGENT)
.nullsFirst();
List<MyBean> list = Arrays.asList(new MyBean(Priority.HIGH),
new MyBean(null), new MyBean(Priority.URGENT));
Priority p = list.stream()
.map(MyBean::getPriority)
.max(order)
.orElse(null);
System.out.println(p); // Urgent
回答4:
You have a list of MyBean
elements that have a priority
attribute and you also need to get the highest element from that list, according to this priority.
If you only need to do this once, then you can use Collections.max
with a suitable comparator and you will be done.
But if you need to get this highest element many times, or, if after getting this highest element, you also need to get the second highest element, then you could perfectly use a PriorityQueue:
Queue<MyBean> queue = new PriorityQueue<>(myComparator);
queue.addAll(myBeanList);
Where myBeanList
is the list containing all your MyBean
instances and myComparator
could be defined as follows, according to your requirements:
Comparator<MyBean> myComparator = Comparator.comparing(
MyBean::getPriority,
Comparator.nullsLast(Comparator.naturalOrder()));
Then, you could use your queue to get the element with the highest priority by means of the PriorityQueue.peek method:
MyBean highest = queue.peek();
This gets but not removes the highest-priority element.
If you want to get and remove the element from the queue instead, you should use the PriorityQueue.poll method:
MyBean highest = queue.poll();
来源:https://stackoverflow.com/questions/43880803/highest-ordinal-enum-value