I have a group of Strings which represent product sizes in which most of them are duplicated in meaning but not name. (IE the size Large has at least 14 different spellings possible, each of which needs to be preserved.) I need to sort these based on the size they represent. Any possible Small value should come before any possible Medium value etc.
The only way I see this being possible is to implement a specific Comparator which contains different Sets grouping each size on the base size it represents. Then I can implement the -1,0,1 relationship by determining which Set that particular size falls into.
Is there a more robust way to accomplish this? Specifically I'm worried about 2 weeks from now when someone comes up with yet another way to spell Large.
edit: to be clear its not the actual comparator I have a question with, its the setup with the sets containing each group. Is this a normal way to handle this situation? How do I future proof it so each new size addition doesn't require a full recompile / deploy?
Custom comparator is the solution. I do not understand why do you worry that this is not robust enough.
A simple approach would be to load the size aliases from a resourcebundle. Some example code (put all the files in the same package):
An interface to encapsulate the size property
public interface Sized {
public String getSize();
}
A product class
public class Product implements Sized {
private final String size;
public Product(String size) {
this.size = size;
}
public String getSize() {
return size;
}
#Override
public String toString() {
return size;
}
}
A comparator that does the magic:
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
public class SizedComparator implements Comparator<Sized> {
// maps size aliases to canonical sizes
private static final Map<String, String> sizes = new HashMap<String, String>();
static {
// create the lookup map from a resourcebundle
ResourceBundle sizesBundle = ResourceBundle
.getBundle(SizedComparator.class.getName());
for (String canonicalSize : sizesBundle.keySet()) {
String[] aliases = sizesBundle.getString(canonicalSize).split(",");
for (String alias : aliases) {
sizes.put(alias, canonicalSize);
}
}
}
#Override
public int compare(Sized s1, Sized s2) {
int result;
String c1 = getCanonicalSize(s1);
String c2 = getCanonicalSize(s2);
if (c1 == null && c2 == null) {
result = 0;
} else if (c1 == null) {
result = -1;
} else if (c2 == null) {
result = 1;
} else {
result = c1.compareTo(c2);
}
return result;
}
private String getCanonicalSize(Sized s1) {
String result = null;
if (s1 != null && s1.getSize() != null) {
result = sizes.get(s1.getSize());
}
return result;
}
}
SizedComparator.properties:
1 = Small,tiny
2 = medium,Average
3 = Large,big,HUGE
A unit test (just for the happy flow):
import org.junit.Before;
import org.junit.Test;
public class FieldSortTest {
private static final String SMALL = "tiny";
private static final String LARGE = "Large";
private static final String MEDIUM = "medium";
private Comparator<Sized> instance;
#Before
public void setup() {
instance = new SizedComparator();
}
#Test
public void testHappy() {
List<Product> products = new ArrayList<Product>();
products.add(new Product(MEDIUM));
products.add(new Product(LARGE));
products.add(new Product(SMALL));
Collections.sort(products, instance);
Assert.assertSame(SMALL, products.get(0).getSize());
Assert.assertSame(MEDIUM, products.get(1).getSize());
Assert.assertSame(LARGE, products.get(2).getSize());
}
}
Note that ResourceBundles are cached automatically. You can reload the ResourceBundle programmatically with:
ResourceBundle.clearCache();
(since Java 1.6). Alternatively you could use some Spring magic to create an auto-reloading message resource.
If reading from a rickety properties file is not cool enough you could quite easily keep your size aliases in a database too.
To impose an arbitrary ordering on a collection of strings (or objects in general), the standard means to do this is to implement a Comparator as you suggest.
Apart from the 'manual' solution you suggest, you could consider comparing the relative edit distance of strings to canonical examples. This will be more flexible in the sense that it will work on alternatives you haven't thought of. But in terms of the work involved, it might be overkill for your application.
Related
My app is sending request to APIs from some other parties.
Every single parties have different return codes that I need to handle.
The result is I need to handle thousands of different return code from those parties.
I am thinking about making a specific class that will hold constant to handle all of them.
It will be like:
public static final Map<String, String> RETURN_CODE_MAP = new HashMap<String, String>(){
{
// these mapping will be thousands of line
put("BANK_A_000","SUCCESS");
put("BANK_A_001","FAILED");
put("BANK_A_002","UNKNOWN");
put("BANK_B_SU","SUCCESS");
put("BANK_B_FA","FAILED");
put("BANK_B_UN","UNKNOWN");
put("BANK_C_00077","SUCCESS");
put("BANK_C_00088","FAILED");
put("BANK_C_00099","UNKNOWN");
put("E-COMMERCE_A_000","SUCCESS");
put("E-COMMERCE_A_001","FAILED");
put("E-COMMERCE_A_002","UNKNOWN");
put("E-COMMERCE_B_000SU","SUCCESS");
put("E-COMMERCE_B_000FA","FAILED");
put("E-COMMERCE_B_000UN","UNKNOWN");
put("E-COMMERCE_C_00077","SUCCESS");
put("E-COMMERCE_C_00088","FAILED");
put("E-COMMERCE_C_00099","UNKNOWN");
}
};
The list of the return code will be thousands of them.
Are there going to be a performance issue?
Can you guys tell me the right way to handle this kind of case? Thank you all
I would suggest using an Enum per party. It allows you to store additional information easier than a map and if used right saves you some performance that could get wasted by string comparison (for example map.get(x).equals("SUCCESS")).
This could look like this:
ResponseStatus.java
public enum ResponseStatus {
SUCCESS,
FAILED,
UNKNOWN;
}
BankA.java
import static my.pkg.ResponseStatus;
public enum BankA {
C_000("000", SUCCESS),
C_001("001", FAILED),
C_002("002", UNKNOWN),
// And so on ...
private final ResponseStatus status;
private final int hashCode;
private BankA(String code, ResponseStatus status) {
this.status = status;
this.hashCode = code.hashCode();
}
public ResponseStatus getStatus() {
return this.status;
}
public static BankA byStaus(String status) {
BankA[] values = values();
int hash = status.hashCode();
for (int n = 0; n < values.length; n++) {
BankA value = values[n];
if (value.hashCode == hash) return value;
}
return null; // No entry found by that code
}
}
Note, that in byStatus I'm comparing the hash codes instead of the strings themselves which is faster than comparing hundreds of strings.
In the end your final check could look like this:
BankA status = BankA.byStatus(response);
if (status != null && status.getStatus() == ResponseStatus.SUCCESS) {
// Do something
}
In the interest of not creating more variables than necessary and cluttering up within the scope of a method that could otherwise have been very slim, I've, instead, created a temporary to hold all of the files I'm going to be referencing throughout the rest of the method.
I dislike this solution because it creates an array object every time it is run when an array object is not necessary to be created.
I could also not use the array or wall of variables, and instead reference the get methods directly, but that creates a lot of redundancy as I am performing the same methods repeatedly, and I dislike that even more.
public void savePrices() {
MFilePrices file[] = {AutoEcon.files().getPrices(), AutoEcon.files().getIntangibles(), AutoEcon.files().getGroups()};
for (String price : sellPrices.keySet()) {
if (EconItem.fromString(price) != null) {
file[0].setPrice(price, sellPrices.get(price).getExpression());
file[0].setBuyRate(price, sellPrices.get(price).getBuyRate());
} else if (file[1].getLabels().contains(price)) {
file[1].setPrice(price, sellPrices.get(price).getExpression());
file[1].setBuyRate(price, sellPrices.get(price).getBuyRate());
} else if (file[2].getLabels().contains(price)) {
file[2].setPrice(price, sellPrices.get(price).getExpression());
file[2].setBuyRate(price, sellPrices.get(price).getBuyRate());
}
}
}
public Double setExpression(String id, String expr) {
savePrices();
MFilePrices file[] = {AutoEcon.files().getPrices(), AutoEcon.files().getIntangibles(), AutoEcon.files().getGroups()};
if (EconItem.fromString(id) != null)
file[0].setPrice(id, expr);
else if (file[1].getLabels().contains(id))
file[1].setPrice(id, expr);
else if (file[2].getLabels().contains(id))
file[2].setPrice(id, expr);
else return null;
sellPrices.clear();
total=0;
loadPrices(AutoEcon.plugin());
return sellPrices.get(id).getPrice();
}
Another solution could be to create an array within the FilePool class where I'm getting the files from, which contains those three configuration files, or a method which puts them into an array and sends over the array. However, the latter just moves the problem over to another class, and the former is still creating a single array that is not totally necessary.
Both of these solutions just moves the problem from one class to another.
public class FilePool {
private Config config;
private Prices prices;
private Intangibles i;
private Groups groups;
private Logs econLogs;
private ItemAliases a;
public FilePool(AutoEcon pl) {
config = new Config(pl);
prices = new Prices(pl);
i = new Intangibles(pl);
econLogs = new Logs(pl);
a = new ItemAliases(pl);
new ReadMe(pl);
}
public Config getConfig() {
return config;
}
public Prices getPrices() {
return prices;
}
public Groups getGroups() {
return groups;
}
public Intangibles getIntangibles() {
return i;
}
public Logs getLogs() {
return econLogs;
}
public ItemAliases getAliases() {
return a;
}
}
(Ignore the dumb variable names in the FilePool class, I just loved the fact that they all line up so perfectly. Will be naming appropriately before publishing)
I know I'm being a bit over-anal about this tiny thing that won't affect the running program at all, but after being constantly harassed for every minor detail of my code by my colleagues in the past, I've grown to be a bit of a perfectionist.
Thanks to anyone who spent their time reading this. <3
The creation of the array is not a problem. Resources to create an array are meaningless. What is more of a problem is that anyone reading your code will struggle to understand what the magic indices represent without referring back to the array. Which means that you should turn them into named constants which will complicate your code even further.
Much better is to have clear variable names that represent what each element represents. Also a good idea to iterate through the map so you can avoid getting the value for each item:
FilePool files = AutoEcon.files();
final MFilePrices prices = files.getPrices();
final MFilePrices intangibles = files.getIntangibles();
final MFilePrices groups = files.getGroups();
sellPrices.forEach((price, value) -> {
if (EconItem.fromString(price) != null) {
setPriceAndBuyRate(prices, price, value);
} else if (intangibles.getLabels().contains(price)) {
setPriceAndBuyRate(intangibles, price, value);
} else if (groups.getLabels().contains(price)) {
setPriceAndBuyRate(groups, price, value);
}
});
private void setPriceAndBuyRate(MFilePrices filePrices, Price price, Value value) {
filePrices.setPrice(price, value.getExpression());
filePrices.setBuyRate(price, value.getBuyRate());
}
If you're concerned that the number of variables make the method difficult to read then move the logic for comparing the price to the labels and setting the price and buy rate into a separate class. That's a good practice in any case as it gives the class a single reason to change.
This is my Code and I can't sort my LinkedList.
import java.util.Collections;
import java.util.LinkedList;
import org.omg.CosNaming.NameComponent;
public class Zug implements Comparable<Zug> {
private String abfahrtzeit;
private String zuggattung;
private int zugnummer;
private int fahrtzeit;
public Zug(String zeile) {
String[] teile = zeile.split(";");
this.abfahrtzeit = teile[0];
this.zuggattung = teile[1];
this.zugnummer = Integer.parseInt(teile[2]);
this.fahrtzeit = Integer.parseInt(teile[3]);
}
public String getAbfahrtzeit() {
return abfahrtzeit;
}
public String getZuggattung() {
return zuggattung;
}
public int getZugnummer() {
return zugnummer;
}
public int getFahrtzeit() {
return fahrtzeit;
}
public String toString() {
return this.abfahrtzeit + ";" + this.zuggattung + ";" + this.zugnummer + ";" + this.fahrtzeit;
}
// This is the Problem Block
#Override
public int compareTo (Zug z) {
String datei = "Zuege.dat";
LinkedList<Zug> ll = new LinkedList<Zug>();
Collections.sort( ll, new NameComponent() );
ll = getDaten(datei);
return this.fahrtzeit - z.getFahrtzeit();
}
// End Of Problem Block
private LinkedList<Zug> getDaten(String datei) {
return null;
}
}
As RealSkeptic and matoni write, you must not do anything other in the compareTo(Zug z) method than compare this to z - as the method name implies. compareTo(Zug z) is called by methods sorting a collection whenever they need to compare two elements of that collection. Loading lists of objects in that method doesn't make any sense.
The most simple implementation would be
#Override
public int compareTo(Zug z) {
return this.fahrtzeit - z.getFahrtzeit();
}
You may want to test your code with that implementation. Generate a few example Zug objects, add them to a List, sort that list using Collecitons.sort() and enjoy the result (or give us a meaningful error message).
Java Practices has an elaborate example on how to write a compareTo()-method.
Please note that this implementation is not consistent with equals() (as detailed in the javadoc).
Sorting by fahrzeit might not be the only way to sort your objects and probably should not be the natural order. You probably should implement a Comparator (e.g. FahrzeitComparator, AbfahrtzeitComparator ...) to be able to sort by different criteria. See this example.
Oh, and:
Rewriting your code with English variable names would allow more people to understand what your objects should represent...
I have Arraylist of objects ArrayList<Product> productDatabase. The object contains a String and a double and then these objects will be added to the productDatabase by addProductToDatabase(); as follows:
public void addProductToDatabase(String productName, double dimensions); {
Product newProduct = new Product(ProductName, dimensions);
productDatabase.add(newProduct);
}
I also want to make an Arraylist<ProductCount> productInventory which counts how many Product are accounted for. Before it can add to ArrayList<ProductCount> productInventory however, it should first check if the object details exist in the productDatabase while running addProductToInventory()
public Product getProduct(String name) {
for(i = 0; i < productDatabase.size(); i++)
if(productDatabase.get(i).contains(name) //Error: cannot find symbol- method contains.(java.lang.String)
return productDatabase.get(i)
}
public void addProductToInventory(String productName, double quantity)
{
Product p = getProduct(name);
productCount.add(new ProductCount(o, quantity));
}
Assume that you always have different objects (so nothing will have the same name), but you're always unsure of the dimensions (so when you input the same producttName + dimensions you edit the dimensions in it).
At the end of the day, you have to put all the items in it a large box and report what you've inventoried, so you also have a getProductQuantityTotal() and you have to getProductDimensionTotal()-- as the name suggests, get the total of number of objects you've counted, and the sum of the dimensions.
What do I have to add/change/remove about this code? Don't consider syntax first (because BlueJ checks for common syntax errors and I just typed this by hand). I'm sure that I'm missing a for statement somewhere, and I'm probably misusing contains() because it won't recognise it (I have import java.util.*; and import java.util.ArrayList;)
To answer the question in your post title: How to find a string in an object, for a list of those objects, here is some sample code that does this:
First, I created a trivial object that has a string field:
class ObjectWithStringField {
private final String s;
public ObjectWithStringField(String s) {
this.s = s;
}
public String getString() {
return s;
}
}
And then a code that populates a list of it, and then searches each for the string. There's no magic here, it just iterates through the list until a match is found.
import java.util.List;
import java.util.Arrays;
/**
<P>{#code java StringInObjectInList}</P>
**/
public class StringInObjectInList {
public static final void main(String[] ignored) {
ObjectWithStringField[] owStrArr = new ObjectWithStringField[] {
new ObjectWithStringField("abc"),
new ObjectWithStringField("def"),
new ObjectWithStringField("ghi")};
//Yes this is a List instead of an ArrayList, but you can easily
//change this to work with an ArrayList. I'll leave that to you :)
List<ObjectWithStringField> objWStrList = Arrays.asList(owStrArr);
System.out.println("abc? " + doesStringInObjExistInList("abc", objWStrList));
System.out.println("abcd? " + doesStringInObjExistInList("abcd", objWStrList));
}
private static final boolean doesStringInObjExistInList(String str_toFind, List<ObjectWithStringField> owStrList_toSearch) {
for(ObjectWithStringField owStr : owStrList_toSearch) {
if(owStr.getString().equals(str_toFind)) {
return true;
}
}
return false;
}
}
Output:
[C:\java_code\]java StringInObjectInList
abc? true
abcd? false
In the real world, instead of a List, I'd use a Map<String,ObjectWithStringField>, where the key is that field. Then it'd be as simple as themap.containsKey("abc");. But here it is implemented as you require. You'll still have quite a bit of work to do, to get this working as specifically required by your assignment, but it should get you off to a good start. Good luck!
Is there anyway to check if an enum exists by comparing it to a given string? I can't seem to find any such function. I could just try to use the valueOf method and catch an exception but I'v been taught that catching runtime exceptions is not good practice. Anybody have any ideas?
If I need to do this, I sometimes build a Set<String> of the names, or even my own Map<String,MyEnum> - then you can just check that.
A couple of points worth noting:
Populate any such static collection in a static initializer. Don't use a variable initializer and then rely on it having been executed when the enum constructor runs - it won't have been! (The enum constructors are the first things to be executed, before the static initializer.)
Try to avoid using values() frequently - it has to create and populate a new array each time. To iterate over all elements, use EnumSet.allOf which is much more efficient for enums without a large number of elements.
Sample code:
import java.util.*;
enum SampleEnum {
Foo,
Bar;
private static final Map<String, SampleEnum> nameToValueMap =
new HashMap<String, SampleEnum>();
static {
for (SampleEnum value : EnumSet.allOf(SampleEnum.class)) {
nameToValueMap.put(value.name(), value);
}
}
public static SampleEnum forName(String name) {
return nameToValueMap.get(name);
}
}
public class Test {
public static void main(String [] args)
throws Exception { // Just for simplicity!
System.out.println(SampleEnum.forName("Foo"));
System.out.println(SampleEnum.forName("Bar"));
System.out.println(SampleEnum.forName("Baz"));
}
}
Of course, if you only have a few names this is probably overkill - an O(n) solution often wins over an O(1) solution when n is small enough. Here's another approach:
import java.util.*;
enum SampleEnum {
Foo,
Bar;
// We know we'll never mutate this, so we can keep
// a local copy.
private static final SampleEnum[] copyOfValues = values();
public static SampleEnum forName(String name) {
for (SampleEnum value : copyOfValues) {
if (value.name().equals(name)) {
return value;
}
}
return null;
}
}
public class Test {
public static void main(String [] args)
throws Exception { // Just for simplicity!
System.out.println(SampleEnum.forName("Foo"));
System.out.println(SampleEnum.forName("Bar"));
System.out.println(SampleEnum.forName("Baz"));
}
}
I don't think there's a built-in way to do it without catching exceptions. You could instead use something like this:
public static MyEnum asMyEnum(String str) {
for (MyEnum me : MyEnum.values()) {
if (me.name().equalsIgnoreCase(str))
return me;
}
return null;
}
Edit: As Jon Skeet notes, values() works by cloning a private backing array every time it is called. If performance is critical, you may want to call values() only once, cache the array, and iterate through that.
Also, if your enum has a huge number of values, Jon Skeet's map alternative is likely to perform better than any array iteration.
One of my favorite lib: Apache Commons.
The EnumUtils can do that easily.
Following an example to validate an Enum with that library:
public enum MyEnum {
DIV("div"), DEPT("dept"), CLASS("class");
private final String val;
MyEnum(String val) {
this.val = val;
}
public String getVal() {
return val;
}
}
MyEnum strTypeEnum = null;
// test if String str is compatible with the enum
// e.g. if you pass str = "div", it will return false. If you pass "DIV", it will return true.
if( EnumUtils.isValidEnum(MyEnum.class, str) ){
strTypeEnum = MyEnum.valueOf(str);
}
I don't know why anyone told you that catching runtime exceptions was bad.
Use valueOf and catching IllegalArgumentException is fine for converting/checking a string to an enum.
Based on Jon Skeet answer i've made a class that permits to do it easily at work:
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**
* <p>
* This permits to easily implement a failsafe implementation of the enums's valueOf
* Better use it inside the enum so that only one of this object instance exist for each enum...
* (a cache could solve this if needed)
* </p>
*
* <p>
* Basic usage exemple on an enum class called MyEnum:
*
* private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
* public static MyEnum failSafeValueOf(String enumName) {
* return FAIL_SAFE.valueOf(enumName);
* }
*
* </p>
*
* <p>
* You can also use it outside of the enum this way:
* FailSafeValueOf.create(MyEnum.class).valueOf("EnumName");
* </p>
*
* #author Sebastien Lorber <i>(lorber.sebastien#gmail.com)</i>
*/
public class FailSafeValueOf<T extends Enum<T>> {
private final Map<String,T> nameToEnumMap;
private FailSafeValueOf(Class<T> enumClass) {
Map<String,T> map = Maps.newHashMap();
for ( T value : EnumSet.allOf(enumClass)) {
map.put( value.name() , value);
}
nameToEnumMap = ImmutableMap.copyOf(map);
}
/**
* Returns the value of the given enum element
* If the
* #param enumName
* #return
*/
public T valueOf(String enumName) {
return nameToEnumMap.get(enumName);
}
public static <U extends Enum<U>> FailSafeValueOf<U> create(Class<U> enumClass) {
return new FailSafeValueOf<U>(enumClass);
}
}
And the unit test:
import org.testng.annotations.Test;
import static org.testng.Assert.*;
/**
* #author Sebastien Lorber <i>(lorber.sebastien#gmail.com)</i>
*/
public class FailSafeValueOfTest {
private enum MyEnum {
TOTO,
TATA,
;
private static final FailSafeValueOf<MyEnum> FAIL_SAFE = FailSafeValueOf.create(MyEnum.class);
public static MyEnum failSafeValueOf(String enumName) {
return FAIL_SAFE.valueOf(enumName);
}
}
#Test
public void testInEnum() {
assertNotNull( MyEnum.failSafeValueOf("TOTO") );
assertNotNull( MyEnum.failSafeValueOf("TATA") );
assertNull( MyEnum.failSafeValueOf("TITI") );
}
#Test
public void testInApp() {
assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TOTO") );
assertNotNull( FailSafeValueOf.create(MyEnum.class).valueOf("TATA") );
assertNull( FailSafeValueOf.create(MyEnum.class).valueOf("TITI") );
}
}
Notice that i used Guava to make an ImmutableMap but actually you could use a normal map i think since the map is never returned...
Most of the answers suggest either using a loop with equals to check if the enum exists or using try/catch with enum.valueOf(). I wanted to know which method is faster and tried it. I am not very good at benchmarking, so please correct me if I made any mistakes.
Heres the code of my main class:
package enumtest;
public class TestMain {
static long timeCatch, timeIterate;
static String checkFor;
static int corrects;
public static void main(String[] args) {
timeCatch = 0;
timeIterate = 0;
TestingEnum[] enumVals = TestingEnum.values();
String[] testingStrings = new String[enumVals.length * 5];
for (int j = 0; j < 10000; j++) {
for (int i = 0; i < testingStrings.length; i++) {
if (i % 5 == 0) {
testingStrings[i] = enumVals[i / 5].toString();
} else {
testingStrings[i] = "DOES_NOT_EXIST" + i;
}
}
for (String s : testingStrings) {
checkFor = s;
if (tryCatch()) {
++corrects;
}
if (iterate()) {
++corrects;
}
}
}
System.out.println(timeCatch / 1000 + "us for try catch");
System.out.println(timeIterate / 1000 + "us for iterate");
System.out.println(corrects);
}
static boolean tryCatch() {
long timeStart, timeEnd;
timeStart = System.nanoTime();
try {
TestingEnum.valueOf(checkFor);
return true;
} catch (IllegalArgumentException e) {
return false;
} finally {
timeEnd = System.nanoTime();
timeCatch += timeEnd - timeStart;
}
}
static boolean iterate() {
long timeStart, timeEnd;
timeStart = System.nanoTime();
TestingEnum[] values = TestingEnum.values();
for (TestingEnum v : values) {
if (v.toString().equals(checkFor)) {
timeEnd = System.nanoTime();
timeIterate += timeEnd - timeStart;
return true;
}
}
timeEnd = System.nanoTime();
timeIterate += timeEnd - timeStart;
return false;
}
}
This means, each methods run 50000 times the lenght of the enum
I ran this test multiple times, with 10, 20, 50 and 100 enum constants.
Here are the results:
10: try/catch: 760ms | iteration: 62ms
20: try/catch: 1671ms | iteration: 177ms
50: try/catch: 3113ms | iteration: 488ms
100: try/catch: 6834ms | iteration: 1760ms
These results were not exact. When executing it again, there is up to 10% difference in the results, but they are enough to show, that the try/catch method is far less efficient, especially with small enums.
Since Java 8, we could use streams instead of for loops. Also, it might be apropriate to return an Optional if the enum does not have an instance with such a name.
I have come up with the following three alternatives on how to look up an enum:
private enum Test {
TEST1, TEST2;
public Test fromNameOrThrowException(String name) {
return Arrays.stream(values())
.filter(e -> e.name().equals(name))
.findFirst()
.orElseThrow(() -> new IllegalArgumentException("No enum with name " + name));
}
public Test fromNameOrNull(String name) {
return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst().orElse(null);
}
public Optional<Test> fromName(String name) {
return Arrays.stream(values()).filter(e -> e.name().equals(name)).findFirst();
}
}
Just use valueOf() method.
If the value doesn't exist, it throws IllegalArgumentException and you can catch it like that:
boolean isSettingCodeValid = true;
try {
SettingCode.valueOf(settingCode.toUpperCase());
} catch (IllegalArgumentException e) {
// throw custom exception or change the isSettingCodeValid value
isSettingCodeValid = false;
}
You can also use Guava and do something like this:
// This method returns enum for a given string if it exists, otherwise it returns default enum.
private MyEnum getMyEnum(String enumName) {
// It is better to return default instance of enum instead of null
return hasMyEnum(enumName) ? MyEnum.valueOf(enumName) : MyEnum.DEFAULT;
}
// This method checks that enum for a given string exists.
private boolean hasMyEnum(String enumName) {
return Iterables.any(Arrays.asList(MyEnum.values()), new Predicate<MyEnum>() {
public boolean apply(MyEnum myEnum) {
return myEnum.name().equals(enumName);
}
});
}
In second method I use guava (Google Guava) library which provides very useful Iterables class. Using the Iterables.any() method we can check if a given value exists in a list object. This method needs two parameters: a list and Predicate object. First I used Arrays.asList() method to create a list with all enums. After that I created new Predicate object which is used to check if a given element (enum in our case) satisfies the condition in apply method. If that happens, method Iterables.any() returns true value.
Using java 8, you can do something like the below to check if it is valid.
Stream.of(MyEnum.values())
.map(MyEnum::name)
.collect(Collectors.toList()).contains(<STRING_YOU_WANT_TO_VALIDATE>)
Here is what I use to check if an enum constant with given name exists:
java.util.Arrays.stream(E.values()).map(E::name).toList().contains("");
(Suppose your enum is called E.) Here inside "" you should put a name of an enum constant for which you wish to check if it is defined in the enum or not.
This is certainly not the best possible solution since it converts an array into Stream and then again into List, but is nice and short and it works fine for me.
As other people mentioned, since you asked this question in 2009., this will not work in your case (unless you migrated to a newer version of Java) since in 2009. Java did not support features used in this answer. But I am posting anyway in case someone with newer version of Java wants to do this.