Enum-getter vs. Getter for each stat - java

I am currently processing a huge data input, including a lot of values, which I want to receive in getters for later use.
After writing a few methodes, I wondered if it might be a better idea to just use one get Method, with an enum-class containing all possible values, e.g.
public double getStat(StatType st) {
if(st != null)
return st.getValue();
}
instead of
public double getAvgReflection() {
return ...
}
public double getMaxLifesteal() {
return ...
}
Is there any convention for using either of the two possibilities?
Any dis/advantages?
Thanks in advance!

Using an Enum maxes it easier to add new stats, just add a constant in the Enum.
But all stats need to behave the same way, i.e. be doubles in your example. What happens if you want to add an int stat?

The "convention" you are asking about really boils down to the use and definition of your values. When you make the values Enums, then you must handle them as that type. Meaning, the fields in your class would have to be defined as such:
private MyEnum avgReflection;
private MyEnum maxLifesteal;
...
public MyEnum getAvgReflection {
return this.avgReflection;
}
And so forth.
Now, you could have your Enums return double values, but these values are static. I don't think you are concerned about static values, but, instead, perhaps a static set of values.
You then have two possible options: declare all possible parameters as fields, or create one aggregate field to hold all values and then use an Enum as an index:
public enum MyEnum {
averageReflection(0),
maximumLifeSteal(1);
private int value;
private MyEnum(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
...
private double[] attributes = new double[100]; // arbitrary initialization
public double getAttribute(MyEnum attribute) {
return this.attributes[attribute.getValue()];
}
The two restrictions on using an array (assuming you want primitive values and you are concerned about performance) is that all the values must be the same type, and the number of attributes will be set at compile time.
Additionally, you may just want to use a Map<String,double> or Map<MyEnum,double>, or even Map<MyEnum, Object>. A Map type will give you the ability to maintain a dynamically-sized set and possibly holding multiple types as well (with the costly overhead of converting your values).
You should base your decision on the amount of attributes you need to keep, the kind of overhead you are willing to tolerate, and your style.

Related

Require input regarding JAVA ENUMS

Consider the following
public enum tc implements {
NORESULTS(0), GOOD_RESULTS(1), EXCELLENT_RESULTS(2), NO_DATA_AVAILABLE(5), SOME_OTHER_VALUE(4);
private final Integer value;
// Code for the constructor, getters and setters for the value****
The enum tc values correspond to the testValue in the below class.
public class TestData {
private int testID;
private String testName;
private int testValue;
....
...
}
In the Results class, the TestDataList has to be sorted by a different order of ranking rather than testValue.For example Excellent followed by Good Results followed by NoResults etc..
public class Results {
List<TestData> TestDataList = getTestData();
I can code for the comparator etc..the question is since I require a different ordering for the enums which of the following two options is better
a) add private int rankTestValue in the enum tc. This option may require that I have to write a method getRank(int value) that would return the corresponding rankTestValue based on the value.
OR
b) add in Results class a map Map tcRankMap = new HashMap();. Populate this map with key values like (2,1) (1,2) corresponding to (enum values, ranking).For example (2,1) would be Excellent_Results has first ranking etc.
Which of these two options would be better. If there are better solutions then please let me know.
Option (a) looks better and according to Object Oriented Analysis and Design.
The good news is that the is a question of implementation detail which can be encapsulated into your Comparator anyway, so it doesn't matter so much.
As for style and readability, I would prefer (a), but it's down to personal preference.
There is also a solution (c) - use the ordinal(), and then sort them according to rank. Just add a comment to make it clear
public enum tc implements {
// NB: enum values are sorted according to rank
EXCELLENT_RESULTS(2),
GOOD_RESULTS(1),
NORESULTS(0),
NO_DATA_AVAILABLE(5),
SOME_OTHER_VALUE(4);
private final Integer value;
// Code for the constructor, getters and setters for the value****
}
Your first option would look like this:
enum TestScore {
EXCELLENT(5),
NO_RESULT(2),
POOR(1);
private final int order;
private TestScore(int order) {
this.order = order;
}
public int compareOrderTo(TestScore other) {
return this.order - other.order;
}
}
You could then add a comparison method to TestData
public int compareTestScore(TestData other) {
return this.testScore.compareOrderTo(other.testScore);
}
And sort your list with:
Collections.sort(testData, TestData::compareTestScore);
The problem with this is that the order field is really completely arbitrary and needs to be updated each time you add a new entry. However that's definitely better and more explicit than using the natural ordering of the enum (i.e. it's ordinal value which should be entirely incidental to avoid fragility).

Convert from String to a Java enum with large amount of values [duplicate]

This question already has answers here:
How to get an enum value from a string value in Java
(32 answers)
Closed 7 years ago.
Let's say I have an enum with 100 values. For simplicity's sake, take the following example:
public enum code
{
CODE_1("string1"),
CODE_2("string2"),
CODE_3("string3"),
CODE_4("string4"),
...
}
I want to create a public method to convert strings with a known format (like "string1", "string2"...) to the appropiate enum value CODE_1, CODE_2... Typically this is done by iterating over all values, and if a match is found, return that enum value. (Details can be found in this question.)
However, I'm concerned with reguraly looping over all values. Could this potentially be a huge bottleneck? What if instead of 100 element, there were 1000?
As an exercise for myself, I tried to optimize this lookup with a static map, which can assure O(1) lookup time given any string. I like this extra gimmick, but I only want to include it in my code if it is actually necessary. What are your thoughts and findings on using the iterating method vs the map method?
public enum Code
{
...
//enum values
...
//The string-to-Code map
private static final Map<String,Code> CODE_MAP = populateMap();
private static Map<String,Code> populateMap()
{
Map<String,Code> map = new HashMap<String,Code>();
for(Code c : Code.values())
{
map.put(c.getCode(), c);
}
return map;
}
private String code;
private Code(String code)
{
this.code = code;
}
public String getCode()
{
return this.code;
}
public Code convertFromString(String code)
{
//assume that the given string is actually a key value in the map
return (Code) CODE_MAP.get(code);
}
}
You want a Map<String, Code>, but how to populate it neatly? Enums don't allow you to initialize a static fields before the enum instances are initialized, but there's a neat little trick, called the Initialization-on-demand holder idiom, that makes using a statically initialized map needed for this functionality easy to implement:
public enum Code {
CODE_1("string1"),
CODE_2("string2"),
CODE_3("string3"),
// etc
;
private static class Holder {
static Map<String, Code> CODE_MAP = new HashMap<>();
}
private final String code;
private Code(String code) {
this.code = code;
Holder.CODE_MAP.put(code, this);
}
public String getCode() {
return this.code;
}
public Code convertFromString(String code) {
return Holder.CODE_MAP.get(code);
}
}
This works because the class loader initializes inner static classes before initializing the enum class, so the map is assigned ready to load during enum instance initialization.
No loops. No special code to load the map (done in constructor). Minimal code.
Map is good option : cleaner code and O(1) . If you use for-loop then the best you get is O(n)
Your provided solution is proper implementation.
As you will have to expose only one method and it is more readable.
And it is always good to use Map instead of iterating it manually.
And also as you mentioned the complexity is O(1).
+1 to your question, as it gives a cleaner approach to use enum in some usecases.
If your string code value is a known and consistent format you could avoid the use of a Map and the memory it consumes and construct the CODE enum lookup value on the fly:
public static Code convertFromString(String code) {
return valueOf("CODE_" + code.substring("string".length()));
}
Well, alternatives to the map-solution would be a giant switch-statement (could be automatically generated) or binary-searching an array containing the strings. I don't think either will beat HashMap's performance by a large margin, though if it really matters, you are probably best off by benchmarking.
One thing that has not been mentioned is how Enum.valueOf() let's you turn a String into an enum value, if it has the exact name of one of the enum members. If this is at all a possiblity in your case (looking just at your example, I don't see how Code.CODE_1 could not be easily renamed Code.string1 etc.), I would suggest using it, as it requires no additional coding at all and will hence be the most understandable.

Have Jackson parse Json object as primitive String

Simple question/problem for anybody familiar with building APIs... I have many objects that I prefer to represent as a string rather than a Json object, for simplicity purposes.
For example, I have a date range which I could (and used to) place into an object (with start end end date members), but considering we can have multiple of these ranges, I could instead have this...
['20130210-20130315','20130520-20130524']
Which IMO looks a lot simpler and cleaner than
[
{
"start":"2013-02-10",
"end":"2013-03-15"
},
{
"start":"2013-05-20",
"end":"2013-05-24"
}
]
And this holds for various other objects which are in the main Json object for the service.
My dilemma of just treating them as Strings is that then I lose the ability to mark them with interfaces, which are used all throughout the code. (For instance, this Json in particular might be marked with a "Filter" interface which many methods take in.)
That said, is there any way to satisfy both of these conditions, i.e. having a custom Json object (implementing my own interfaces, etc.) AND have Jackson parse it like a String primitive? I'm hoping this can be accomplished without much work involving custom serialization & deserialization, since I have lots of objects.
Hate duplicating posts, so in an attempt to add some value here -- this does exactly what I want with arrays --
public class MyAwesomeJson extends JacksonObject implements S {
private final String value;
public MyAwesomeJson(String value) {
this.value = value;
}
#JsonValue
public String getValue() {
return value;
}
}
Then to get the array form --
public class MyAwesomeJsonArray extends JacksonObject implements A {
private final Set<MyAwesomeJson> values = Sets.newLinkedHashSet();
public MyAwesomeJsonArray(MyAwesomeJson... values) {
this.values.addAll(Arrays.asList(values));
}
#JsonValue
public Set<MyAwesomeJson> getValues() {
return values;
}
}
System.out.println(new MyAwesomeJsonArray(new MyAwesomeJson("Yellow"),
new MyAwesomeJson("Goodbye")));
["Yellow","Goodbye"]

Using integer values and String identifier in an Enum

So I currently have two enums:
public enum AuthorizationLevel
{
FULL,
HALF,
NONE;
};
public enum DatabaseLoggingLevel
{
HIGH,
MED,
LOW,
AUTH_ONLY,
NONE
}
I want to be able to associate integers with the values in the enums so that I can have code like so:
if(databaseLoggingLevel < ClassName.DatabaseLoggingLevel.HIGH) return;
This is just so that certain logging is disabled when the level is less than HIGH. I thought about making a helper function that returns an integer value associated with each enum value with a switch statement, but that seems hacky. Is there something I am missing?
You can add an integer instance field to your enum, and add a parameterized constructor:
enum AuthorizationLevel {
FULL(1), HALF(2), NONE(3);
private final int level;
private AuthorizationLevel(int level) {
this.level = level;
}
public int getLevel() {
return level;
}
};
Note that there is an ordinal() method which will give you position of your enum constants, but you should avoid using it. Again, quoting Effective Java Item - 31 (Use instance fields instead of ordinal):
If the constants are reordered, the numberOfMusicians method will
break. If you want to add a second enum constant associated with an
int value that you’ve already used, you’re out of luck.
Also, you can’t add a constant for an int value without adding
constants for all intervening int values. For example, suppose you
want to add a constant representing a triple quartet, which consists
of twelve musicians. There is no standard term for an ensemble
consisting of eleven musicians, so you are forced to add a dummy
constant for the unused int value (11). At best, this is ugly. If many
int values are unused, it’s impractical.
Modify the constructor of your enum to add the Integer value.
public enum DatabaseLoggingLevel {
HIGH(1),
MED(2),
LOW(3),
AUTH_ONLY(4),
NONE(5);
private final Integer value;
//For getByValue(). See Rohit's comment
private static final DatabaseLoggingLevel[] VALUES = DatabaseLoggingLevel.values();
private DatabaseLoggingLevel(Integer value) {
this.value = value;
}
public Integer getValue() {
return this.value;
}
//Bonus : getter by value:
public static DatabaseLoggingLevel getByValue(Integer value) {
for(DatabaseLoggingLevel e: VALUES) {
if(e.getValue().equals(value)) {
return e;
}
}
return null;
}
}
public enum DatabaseLoggingLevel
{
HIGH(1),
MED(2),
LOW(3),
AUTH_ONLY(4),
NONE(5)
private int code;
private DatabaseLoggingLevel(int code) {
this.code = code;
}
public int getCode() { return code; }
}
Use the ordinal() method. It gives you the position of the Value in the enum.
Rather than programming this in terms of "enums are numbers", program this in terms of "enums are smart". You can associate numbers with enums (after all, enums are instances of regular Java classes), but make that number private. Then you can add this method to the enum:
public boolean isAboveOrEqualTo(DatabaseLoggingLevel level) {
...
}
Then your conditional would look like this:
if (databaseLoggingLevel.isAboveOrEqualTo(ClassName.DatabaseLoggingLevel.HIGH)) {
...
}
Here is an explanation of how to associate numbers with enums: link.
While both of the answers given already will do what you want I would recommend looking at Log4J as an alternative to rolling your own logging levels
http://www.tutorialspoint.com/log4j/log4j_overview.htm

value of what the enum is currently set to

This should be total obvious, but it isn't. Is there a way to get the value of a enum as in the example below?
example:
public enum Numbers
{
zero, one, two
}
//later on
Numbers numbers = Numbers.zero;
// this is the part that I can not figure out the java equivalent of?
int number = numbers.value();
Does this make sense what I am trying to do?
It is generally considered bad practice to rely on ordinal since it is based on the order of the enum constants. A better solution would be to pass information to each instance. Just write a constructor and method like this:
public enum Number {
ZERO(0), ONE(1), TWO(2);
private int value;
private Number(int value) {
this.value = value;
}
public int value() {
return value;
}
};
Then you use it like you want
Number numbers = Number.ZERO;
int number = numbers.value();
and you won't have to worry about the actual order of the code if you add Numbers later.
Note: It is idiomatic in Java for enum names to be singular and for constants to be uppercase.
Based on your comments it appears you need to map an integer value to your enum to integrate it into another part of your code.
Rather than relying on the ordering you really want to do this yourself:
public enum MyEnum {
ZERO(0), ONE(1), TWO(2);
private int mapping;
MyEnum(int mapping) {
this.mapping = mapping;
}
public int getMapping() {
return mapping;
}
}
You could then compare via:
MyEnum e = MyEnum.ZERO;
if (someInt == e.getMapping()) { ... }
(Or even just MyEnum.ZERO.getMapping() )
You can use the ordinal() method. However, this is discouraged / risky, since if you ever change the order or arrangements of your enums, it will fail. e.g., you change to powers of two
public enum Numbers {
zero, one, two, four
}
then four.ordinal() will be 3.
If you really want a number associated with the enum, you should define your own method, e.g. order() or whatever. In the initial implementation it could return ordinal(), but, when you add four in the future, you can update that method.
p.s. Standard style is to capitalize the enums, e.g. ZERO, ONE, etc...
It depends on what you need. Here are 2 use cases:
1) You will probably need to test the value of an incoming "Numbers". For this use case, a switch case is the best solution.
void myMethod(Numbers n){
switch(n){
case zero:
//do something
break;
case one:
//do something
break;
//etc.
}
}
2) You can calso need to assign a value to each Numbers and be able to retrieve it. For this use case, you can define a constructor for your enum
public enum Numbers
{
zero(0), one(1), two(2);
private int value;
private Numbers(int value){
this.value=value;
}
}
Then, in your code, you can get this value:
void myMethod(Numbers n){
int value = n.getValue();
//do something with this value...
}
Last remark : Your enum names don't follow the java convention. They should be named with uppercase letters and eventually character '_'. ex: ONE, TWO, THREE, ANOTHER_VALUE

Categories