Table / matrix based on enum in Java

I have two enumerations: level with 3 values ​​and criticality with 4 values. The combination of these two cards with one of the 8 values ​​from the priority enum. The display is non-linear and may change in the future.

What is the best way to implement a static function that takes level and criticality and gives priority?

* best is easy to read and understand, easy and safe to change, not performance. Additional solution points that take into account that the input domain may change in the future.

The methods that I have considered so far are:

nested switch..case . Many lines and many patterns. Also error prone if you forgot to return a value in case. Basically the code is as follows:

  switch (bc) { case C1: switch (el) { case E1: return EmergencyPriority.P1; case E2: return EmergencyPriority.P2; case E3: return EmergencyPriority.P3; } case C2: switch (el) { case E1: return EmergencyPriority.P2; case E2: return EmergencyPriority.P3; case E3: return EmergencyPriority.P4; } case C3: switch (el) { case E1: return EmergencyPriority.P4; case E2: return EmergencyPriority.P5; case E3: return EmergencyPriority.P6; } case C4: switch (el) { case E1: return EmergencyPriority.P6; case E2: return EmergencyPriority.P7; case E3: return EmergencyPriority.P8; } } 

Mutikey Map requires an external library, and I did not find a way to insert initial values ​​well without a lot of function calls and compound template keys.


if..else if .. else is basically the same as with the switch, but with more strict code. Less prone to errors.


A two-dimensional array when using enumeration values ​​in the form of integers for array indices that you risk failing if the enum position values ​​change.


Your decision here

+6
java enums matrix
source share
3 answers

1. Define an enumeration with constructors

Inspired by EnumMap 's comment using Object[] , I came up with this solution:

 public enum EmergencyPriority { P1(BusinessCriticality.C1, EmergencyLevel.E1), P2(BusinessCriticality.C1, EmergencyLevel.E2, BusinessCriticality.C2, EmergencyLevel.E1), P3(BusinessCriticality.C1, EmergencyLevel.E3, BusinessCriticality.C2, EmergencyLevel.E2), P4(BusinessCriticality.C2, EmergencyLevel.E3, BusinessCriticality.C3, EmergencyLevel.E1), P5(BusinessCriticality.C3, EmergencyLevel.E2), P6(BusinessCriticality.C3, EmergencyLevel.E3, BusinessCriticality.C4, EmergencyLevel.E1), P7(BusinessCriticality.C4, EmergencyLevel.E2), P8(BusinessCriticality.C4, EmergencyLevel.E3); private static EmergencyPriority[][] PRIORITIES; private EmergencyPriority(BusinessCriticality c, EmergencyLevel l) { addPriority(l, c, this); } private EmergencyPriority(BusinessCriticality c, EmergencyLevel l, BusinessCriticality c2, EmergencyLevel l2) { addPriority(l, c, this); addPriority(l2, c2, this); } private static void addPriority(EmergencyLevel l, BusinessCriticality c, EmergencyPriority p) { if (PRIORITIES == null) { PRIORITIES = new EmergencyPriority[EmergencyLevel.values().length][BusinessCriticality.values().length]; } PRIORITIES[l.ordinal()][c.ordinal()] = p; } public static EmergencyPriority of(BusinessCriticality c, EmergencyLevel l) { return PRIORITIES[l.ordinal()][c.ordinal()]; } } 

2. Define an enumeration and statically initialize the corresponding array

Another solution would be to have a simple enumeration without constructors and statically initialize an array of priorities, which allows them to be reordered as you see fit for reading:

 import static com.package.BusinessCriticality.*; import static com.package.EmergencyLevel.*; public enum EmergencyPriority { P1, P2, P3, P4, P5, P6, P7, P8; private static EmergencyPriority[][] PRIORITIES = new EmergencyPriority[BusinessCriticality.values().length][EmergencyLevel.values().length]; private void define(BusinessCriticality c, EmergencyLevel e) { PRIORITIES[c.ordinal()][e.ordinal()] = this; } static { P1.define(C1, E1); P2.define(C1, E2); P2.define(C2, E1); P3.define(C1, E3); P3.define(C2, E2); P4.define(C2, E3); P4.define(C3, E1); P5.define(C3, E2); P6.define(C3, E3); P6.define(C4, E1); P7.define(C4, E2); P8.define(C4, E3); } public static EmergencyPriority of(BusinessCriticality c, EmergencyLevel e) { return PRIORITIES[c.ordinal()][e.ordinal()]; } } 

And you can have the following JUnit test to ensure that EmergencyPriority has all the combinations if you extend BusinessCriticality or EmergencyLevel:

 @Test public void testEnumCompletude() { for (BusinessCriticality c : BusinessCriticality.values()) { for (EmergencyLevel e : EmergencyLevel.values()) { assertNotNull(String.format("%s/%s combination was forgotten", c, e), EmergencyPriority.of(c, e)); } } } 
0
source share

This structure is probably the "best" way to store your data ("best" = what I assume after you, because with your switch solution I would be fine)

1. EnumMap based solution

 EnumMap<Level, EnumMap<Criticality, Priority>> map = new EnumMap<>(Level.class); EnumMap<Criticality, Priority> c1 = new EnumMap<>(Criticality.class); c1.put(Criticality.E1, Priority.P1); .. map.put(Level.C1, c1); ... 

Then simply write this utility method to access the structure:

 public static Priority priority(Level level, Criticality criticality) { return map.get(level).get(criticality); } 

The advantage of EnumMap is this: it offers the convenience of Map , being quite efficient, since all possible keys are known in advance, so the values ​​can be stored in Object[] .

2. Array Based Solution

You already mentioned this, but I will repeat this idea anyway, because I did it in the past and with the right formatting (this, of course, should never be broken by developers), this approach is very readable and not very error prone.

Remember that formatting is key here:

 Priority[][] map = { // Criticality.E1 Criticality.E2 Criticality.E3 // ---------------------------------------------------------------- /* Level.C1 */ { Priority.P1 , Priority.P2 , Priority.P3 }, /* Level.C2 */ { Priority.P2 , Priority.P3 , Priority.P4 }, /* Level.C3 */ { Priority.P3 , Priority.P4 , Priority.P5 }, /* Level.C4 */ { Priority.P4 , Priority.P5 , Priority.P6 } }; 

Now the method is as follows:

 public static Priority priority(Level level, Criticality criticality) { return map[level.ordinal()][criticality.ordinal()]; } 

To prevent a loss of silence, if someone adds a new enum value in the middle, simply add a unit test, which asserts the expected sequence number for each enumeration literal. The same test can also assert the values ​​of Level.values().length and Criticality.values().length , and you will be safe in the future.

+7
source share

Maybe Map with a tuple of Criticality and Level as a key?

You can create a key class using the custom equal() and hashCode() method, which encapsulates these two values, as shown below:

 public class PriorityTuple{ final Criticality c; final Level l; public PriorityTuple(Criticality c, Level l) { this.c = c; this.l = l; } @Override public boolean equals(Object o) { if (!(o instanceof PriorityTuple)) { return false; } PriorityTuple prioritykey = (PriorityTuple) o; return this.c.equals(prioritykey.c) && this.l.equals(prioritykey.l); } @Override public int hashCode() { int hash = 5; hash = 23 * hash + Objects.hashCode(this.c); hash = 23 * hash + Objects.hashCode(this.l); return hash; } } 

And then create your Map with your entities:

 Map<PriorityTuple, Priority> priorityMap = new HashMap<>(); 

And two methods to simplify add and get:

 // add new entry public static void addPriority(Criticality c, Level l, Priority p) { if (null == c || null == l || null == p) return; // if you want some kind of control priorityMap.put(new PriorityTuple(c, l), p); } // get priority public static Priority priority(Criticality c, Level l) { return priorityMap.get(new PriorityTuple(c, l)); } 

Result:

 addPriority(Criticality.C1, Level.L1, Priority.P1); addPriority(Criticality.C1, Level.L2, Priority.P2); addPriority(Criticality.C1, Level.L3, Priority.P3); addPriority(Criticality.C2, Level.L1, Priority.P2); addPriority(Criticality.C2, Level.L2, Priority.P3); addPriority(Criticality.C2, Level.L3, Priority.P4); // and so on... // retrieving values... System.out.println(priority(Criticality.C1, Level.L1)); // print P1 System.out.println(priority(Criticality.C4, Level.L3)); // null if not exist 

In this case, you can continue to add additional entries for your types of transfers in the future without breaking the code (?)

+1
source share

All Articles