Highest ordinal enum value

懵懂的女人 提交于 2021-02-18 18:47:25

问题


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 Listof 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:

  1. Look at every bean
  2. Take its priority
  3. Discard null priorities
  4. Pick the maximum priority
  5. 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

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!