Why doesn't Set provide an operation to get an element that equals another element?
Set<Foo> set = ...;
...
Foo foo = new Foo(1, 2, 3);
Foo bar = set.get(foo); // get the Foo element from the Set that equals foo
I can ask whether the Set contains an element equal to bar, so why can't I get that element? :(
To clarify, the equals method is overridden, but it only checks one of the fields, not all. So two Foo objects that are considered equal can actually have different values, that's why I can't just use foo.
To answer the precise question "Why doesn't Set provide an operation to get an element that equals another element?", the answer would be: because the designers of the collection framework were not very forward looking. They didn't anticipate your very legitimate use case, naively tried to "model the mathematical set abstraction" (from the javadoc) and simply forgot to add the useful get() method.
Now to the implied question "how do you get the element then": I think the best solution is to use a Map<E,E> instead of a Set<E>, to map the elements to themselves. In that way, you can efficiently retrieve an element from the "set", because the get() method of the Map will find the element using an efficient hash table or tree algorithm. If you wanted, you could write your own implementation of Set that offers the additional get() method, encapsulating the Map.
The following answers are in my opinion bad or wrong:
"You don't need to get the element, because you already have an equal object": the assertion is wrong, as you already showed in the question. Two objects that are equal still can have different state that is not relevant to the object equality. The goal is to get access to this state of the element contained in the Set, not the state of the object used as a "query".
"You have no other option but to use the iterator": that is a linear search over a collection which is totally inefficient for large sets (ironically, internally the Set is organized as hash map or tree that could be queried efficiently). Don't do it! I have seen severe performance problems in real-life systems by using that approach. In my opinion what is terrible about the missing get() method is not so much that it is a bit cumbersome to work around it, but that most programmers will use the linear search approach without thinking of the implications.
There would be no point of getting the element if it is equal. A Map is better suited for this usecase.
If you still want to find the element you have no other option but to use the iterator:
public static void main(String[] args) {
Set<Foo> set = new HashSet<Foo>();
set.add(new Foo("Hello"));
for (Iterator<Foo> it = set.iterator(); it.hasNext(); ) {
Foo f = it.next();
if (f.equals(new Foo("Hello")))
System.out.println("foo found");
}
}
static class Foo {
String string;
Foo(String string) {
this.string = string;
}
#Override
public int hashCode() {
return string.hashCode();
}
#Override
public boolean equals(Object obj) {
return string.equals(((Foo) obj).string);
}
}
If you have an equal object, why do you need the one from the set? If it is "equal" only by a key, an Map would be a better choice.
Anyway, the following will do it:
Foo getEqual(Foo sample, Set<Foo> all) {
for (Foo one : all) {
if (one.equals(sample)) {
return one;
}
}
return null;
}
With Java 8 this can become a one liner:
return all.stream().filter(sample::equals).findAny().orElse(null);
Default Set in Java is, unfortunately, not designed to provide a "get" operation, as jschreiner accurately explained.
The solutions of using an iterator to find the element of interest (suggested by dacwe) or to remove the element and re-add it with its values updated (suggested by KyleM), could work, but can be very inefficient.
Overriding the implementation of equals so that non-equal objects are "equal", as stated correctly by David Ogren, can easily cause maintenance problems.
And using a Map as an explicit replacement (as suggested by many), imho, makes the code less elegant.
If the goal is to get access to the original instance of the element contained in the set (hope I understood correctly your use case), here is another possible solution.
I personally had your same need while developing a client-server videogame with Java. In my case, each client had copies of the components stored in the server and the problem was whenever a client needed to modify an object of the server.
Passing an object through the internet meant that the client had different instances of that object anyway. In order to match this "copied" instance with the original one, I decided to use Java UUIDs.
So I created an abstract class UniqueItem, which automatically gives a random unique id to each instance of its subclasses.
This UUID is shared between the client and the server instance, so this way it could be easy to match them by simply using a Map.
However directly using a Map in a similar usecase was still inelegant. Someone might argue that using an Map might be more complicated to mantain and handle.
For these reasons I implemented a library called MagicSet, that makes the usage of an Map "transparent" to the developer.
https://github.com/ricpacca/magicset
Like the original Java HashSet, a MagicHashSet (which is one of the implementations of MagicSet provided in the library) uses a backing HashMap, but instead of having elements as keys and a dummy value as values, it uses the UUID of the element as key and the element itself as value. This does not cause overhead in the memory use compared to a normal HashSet.
Moreover, a MagicSet can be used exactly as a Set, but with some more methods providing additional functionalities, like getFromId(), popFromId(), removeFromId(), etc.
The only requirement to use it is that any element that you want to store in a MagicSet needs to extend the abstract class UniqueItem.
Here is a code example, imagining to retrieve the original instance of a city from a MagicSet, given another instance of that city with the same UUID (or even just its UUID).
class City extends UniqueItem {
// Somewhere in this class
public void doSomething() {
// Whatever
}
}
public class GameMap {
private MagicSet<City> cities;
public GameMap(Collection<City> cities) {
cities = new MagicHashSet<>(cities);
}
/*
* cityId is the UUID of the city you want to retrieve.
* If you have a copied instance of that city, you can simply
* call copiedCity.getId() and pass the return value to this method.
*/
public void doSomethingInCity(UUID cityId) {
City city = cities.getFromId(cityId);
city.doSomething();
}
// Other methods can be called on a MagicSet too
}
If your set is in fact a NavigableSet<Foo> (such as a TreeSet), and Foo implements Comparable<Foo>, you can use
Foo bar = set.floor(foo); // or .ceiling
if (foo.equals(bar)) {
// use bar…
}
(Thanks to #eliran-malka’s comment for the hint.)
With Java 8 you can do:
Foo foo = set.stream().filter(item->item.equals(theItemYouAreLookingFor)).findFirst().get();
But be careful, .get() throws a NoSuchElementException, or you can manipulate a Optional item.
Convert set to list, and then use get method of list
Set<Foo> set = ...;
List<Foo> list = new ArrayList<Foo>(set);
Foo obj = list.get(0);
Why:
It seems that Set plays a useful role in providing a means of comparison. It is designed not to store duplicate elements.
Because of this intention/design, if one were to get() a reference to the stored object, then mutate it, it is possible that the design intentions of Set could be thwarted and could cause unexpected behavior.
From the JavaDocs
Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.
How:
Now that Streams have been introduced one can do the following
mySet.stream()
.filter(object -> object.property.equals(myProperty))
.findFirst().get();
Object objectToGet = ...
Map<Object, Object> map = new HashMap<Object, Object>(set.size());
for (Object o : set) {
map.put(o, o);
}
Object objectFromSet = map.get(objectToGet);
If you only do one get this will not be very performing because you will loop over all your elements but when performing multiple retrieves on a big set you will notice the difference.
you can use Iterator class
import java.util.Iterator;
import java.util.HashSet;
public class MyClass {
public static void main(String[ ] args) {
HashSet<String> animals = new HashSet<String>();
animals.add("fox");
animals.add("cat");
animals.add("dog");
animals.add("rabbit");
Iterator<String> it = animals.iterator();
while(it.hasNext()) {
String value = it.next();
System.out.println(value);
}
}
}
it looks like the proper object to use is the Interner from guava :
Provides equivalent behavior to String.intern() for other immutable
types. Common implementations are available from the Interners
class.
It also has a few very interesting levers, like concurrencyLevel, or the type of references used (it might be worth noting that it doesn't offer a SoftInterner which I could see as more useful than a WeakInterner).
I know, this has been asked and answered long ago, however if anyone is interested, here is my solution - custom set class backed by HashMap:
http://pastebin.com/Qv6S91n9
You can easily implement all other Set methods.
Been there done that!! If you are using Guava a quick way to convert it to a map is:
Map<Integer,Foo> map = Maps.uniqueIndex(fooSet, Foo::getKey);
If you want nth Element from HashSet, you can go with below solution,
here i have added object of ModelClass in HashSet.
ModelClass m1 = null;
int nth=scanner.nextInt();
for(int index=0;index<hashset1.size();index++){
m1 = (ModelClass) itr.next();
if(nth == index) {
System.out.println(m1);
break;
}
}
If you look at the first few lines of the implementation of java.util.HashSet you will see:
public class HashSet<E>
....
private transient HashMap<E,Object> map;
So HashSet uses HashMap interally anyway, which means that if you just use a HashMap directly and use the same value as the key and the value you will get the effect you want and save yourself some memory.
Because any particular implementation of Set may or may not be random access.
You can always get an iterator and step through the Set, using the iterators' next() method to return the result you want once you find the equal element. This works regardless of the implementation. If the implementation is NOT random access (picture a linked-list backed Set), a get(E element) method in the interface would be deceptive, since it would have to iterate the collection to find the element to return, and a get(E element) would seem to imply this would be necessary, that the Set could jump directly to the element to get.
contains() may or may not have to do the same thing, of course, depending on the implementation, but the name doesn't seem to lend itself to the same sort of misunderstandings.
Yes, use HashMap ... but in a specialised way: the trap I foresee in trying to use a HashMap as a pseudo-Set is the possible confusion between "actual" elements of the Map/Set, and "candidate" elements, i.e. elements used to test whether an equal element is already present. This is far from foolproof, but nudges you away from the trap:
class SelfMappingHashMap<V> extends HashMap<V, V>{
#Override
public String toString(){
// otherwise you get lots of "... object1=object1, object2=object2..." stuff
return keySet().toString();
}
#Override
public V get( Object key ){
throw new UnsupportedOperationException( "use tryToGetRealFromCandidate()");
}
#Override
public V put( V key, V value ){
// thorny issue here: if you were indavertently to `put`
// a "candidate instance" with the element already in the `Map/Set`:
// these will obviously be considered equivalent
assert key.equals( value );
return super.put( key, value );
}
public V tryToGetRealFromCandidate( V key ){
return super.get(key);
}
}
Then do this:
SelfMappingHashMap<SomeClass> selfMap = new SelfMappingHashMap<SomeClass>();
...
SomeClass candidate = new SomeClass();
if( selfMap.contains( candidate ) ){
SomeClass realThing = selfMap.tryToGetRealFromCandidate( candidate );
...
realThing.useInSomeWay()...
}
But... you now want the candidate to self-destruct in some way unless the programmer actually immediately puts it in the Map/Set... you'd want contains to "taint" the candidate so that any use of it unless it joins the Map makes it "anathema". Perhaps you could make SomeClass implement a new Taintable interface.
A more satisfactory solution is a GettableSet, as below. However, for this to work you have either to be in charge of the design of SomeClass in order to make all constructors non-visible (or... able and willing to design and use a wrapper class for it):
public interface NoVisibleConstructor {
// again, this is a "nudge" technique, in the sense that there is no known method of
// making an interface enforce "no visible constructor" in its implementing classes
// - of course when Java finally implements full multiple inheritance some reflection
// technique might be used...
NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet );
};
public interface GettableSet<V extends NoVisibleConstructor> extends Set<V> {
V getGenuineFromImpostor( V impostor ); // see below for naming
}
Implementation:
public class GettableHashSet<V extends NoVisibleConstructor> implements GettableSet<V> {
private Map<V, V> map = new HashMap<V, V>();
#Override
public V getGenuineFromImpostor(V impostor ) {
return map.get( impostor );
}
#Override
public int size() {
return map.size();
}
#Override
public boolean contains(Object o) {
return map.containsKey( o );
}
#Override
public boolean add(V e) {
assert e != null;
V result = map.put( e, e );
return result != null;
}
#Override
public boolean remove(Object o) {
V result = map.remove( o );
return result != null;
}
#Override
public boolean addAll(Collection<? extends V> c) {
// for example:
throw new UnsupportedOperationException();
}
#Override
public void clear() {
map.clear();
}
// implement the other methods from Set ...
}
Your NoVisibleConstructor classes then look like this:
class SomeClass implements NoVisibleConstructor {
private SomeClass( Object param1, Object param2 ){
// ...
}
static SomeClass getOrCreate( GettableSet<SomeClass> gettableSet, Object param1, Object param2 ) {
SomeClass candidate = new SomeClass( param1, param2 );
if (gettableSet.contains(candidate)) {
// obviously this then means that the candidate "fails" (or is revealed
// to be an "impostor" if you will). Return the existing element:
return gettableSet.getGenuineFromImpostor(candidate);
}
gettableSet.add( candidate );
return candidate;
}
#Override
public NoVisibleConstructor addOrGetExisting( GettableSet<? extends NoVisibleConstructor> gettableSet ){
// more elegant implementation-hiding: see below
}
}
PS one technical issue with such a NoVisibleConstructor class: it may be objected that such a class is inherently final, which may be undesirable. Actually you could always add a dummy parameterless protected constructor:
protected SomeClass(){
throw new UnsupportedOperationException();
}
... which would at least let a subclass compile. You'd then have to think about whether you need to include another getOrCreate() factory method in the subclass.
Final step is an abstract base class (NB "element" for a list, "member" for a set) like this for your set members (when possible - again, scope for using a wrapper class where the class is not under your control, or already has a base class, etc.), for maximum implementation-hiding:
public abstract class AbstractSetMember implements NoVisibleConstructor {
#Override
public NoVisibleConstructor
addOrGetExisting(GettableSet<? extends NoVisibleConstructor> gettableSet) {
AbstractSetMember member = this;
#SuppressWarnings("unchecked") // unavoidable!
GettableSet<AbstractSetMembers> set = (GettableSet<AbstractSetMember>) gettableSet;
if (gettableSet.contains( member )) {
member = set.getGenuineFromImpostor( member );
cleanUpAfterFindingGenuine( set );
} else {
addNewToSet( set );
}
return member;
}
abstract public void addNewToSet(GettableSet<? extends AbstractSetMember> gettableSet );
abstract public void cleanUpAfterFindingGenuine(GettableSet<? extends AbstractSetMember> gettableSet );
}
... usage is fairly obvious (inside your SomeClass's static factory method):
SomeClass setMember = new SomeClass( param1, param2 ).addOrGetExisting( set );
The contract of the hash code makes clear that:
"If two objects are equal according to the Object method, then calling the hashCode method on each of the two objects must produce the same integer result."
So your assumption:
"To clarify, the equals method is overridden, but it only checks one of
the fields, not all. So two Foo objects that are considered equal can
actually have different values, that's why I can't just use foo."
is wrong and you are breaking the contract. If we look at the "contains" method of Set interface, we have that:
boolean contains(Object o);
Returns true if this set contains the specified element. More
formally, returns true if and only if this set contains an element
"e" such that o==null ? e==null : o.equals(e)
To accomplish what you want, you can use a Map where you define the key and store your element with the key that defines how objects are different or equal to each other.
Here's what you can do if you have a NavigableSet (e.g. a TreeSet):
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
The things are slightly trickier for HashSet and its descendants like LinkedHashSet:
import java.util.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test {
private static final Field mapField;
private static final Method hashMethod;
private static final Method getNodeMethod;
private static final Field keyField;
static {
try {
mapField = HashSet.class.getDeclaredField("map");
mapField.setAccessible(true);
hashMethod = HashMap.class.getDeclaredMethod("hash", Object.class);
hashMethod.setAccessible(true);
getNodeMethod = HashMap.class.getDeclaredMethod("getNode",
Integer.TYPE, Object.class);
getNodeMethod.setAccessible(true);
keyField = Class.forName("java.util.HashMap$Node").getDeclaredField("key");
keyField.setAccessible(true);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(HashSet<E> set, E key) {
try {
Object map = mapField.get(set);
Object hash = hashMethod.invoke(null, key);
Object node = getNodeMethod.invoke(map, hash, key);
if (node == null)
return null;
#SuppressWarnings("unchecked")
E result = (E)keyField.get(node);
return result;
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
public static <E> E get(NavigableSet<E> set, E key) {
return set.tailSet(key, true).floor(key);
}
public static void main(String[] args) {
HashSet<Integer> s = new HashSet<>();
// HashSet<Integer> s = new LinkedHashSet<>();
// TreeSet<Integer> s = new TreeSet<>();
for (int i = 0; i < 100_000; i++)
s.add(i);
Integer key = java.awt.event.KeyEvent.VK_FIND;
Integer hidden = get(s, key);
System.out.println(key);
System.out.println(hidden);
System.out.println(key.equals(hidden));
System.out.println(key == hidden);
}
}
Quick helper method that might address this situation:
<T> T onlyItem(Collection<T> items) {
if (items.size() != 1)
throw new IllegalArgumentException("Collection must have single item; instead it has " + items.size());
return items.iterator().next();
}
Try using an array:
ObjectClass[] arrayName = SetOfObjects.toArray(new ObjectClass[setOfObjects.size()]);
Following can be an approach
SharedPreferences se_get = getSharedPreferences("points",MODE_PRIVATE);
Set<String> main = se_get.getStringSet("mydata",null);
for(int jk = 0 ; jk < main.size();jk++)
{
Log.i("data",String.valueOf(main.toArray()[jk]));
}
Say I want to convert the below implementation in Java to C#, so that I don't have to cast the returned value at runtime, where the casting should already be handled in the get method.
why not create setters and getters, if you ask? simply because I plan to have 50-100+ attributes and I don't want to create setters and getters for every attributes.
[c#] - what i want to end up doing in c#
string name = playerA.getAttribute(Attribute.NAME);
int age = playerA.getAttribute(Attribute.AGE);
Currently I can't unless I cast the returned value to the correct type. But can I do that casting in side the get method before returning?
[Java] - anyhow, this is the current java implementation that works without casting
//setting attributes
playerA.setAttribute(Attribute.NAME, "Tom");
entityB.setAttribute(Attribute.AGE, 4);
...
//getting the attribute without casting
string name = playerA.getAttribute(PlayerAttribute.NAME);
int age = playerB.getAttribute(PlayerAttribute.AGE);
The method inside a Player/Entity is setup like this to get attributes
[Java]
public <E> E getAttribute(Attribute attr){
//atrributeRepository is EnumMap<Attribute, Object>
//how I will get my attribute value type at runtime
Object result = attributeRepositoryMap.get(attr);
//the enumMap will only ever hold these three type for this example
if(result instanceof Boolean){ return (E) (Boolean) result; }
if(result instanceof String){ return (E) (String) result; }
if(result instanceof Integer){ return (E) (Integer) result; }
return null;
//say all checks are in place and null will never be reach
}
The closest I was able to get in c# is this.
[c#] - though I can deal with it, i would like to prevent casting
string name = (string) getAttribute<string>(Attribute.NAME);
int age = (int) getAttribute<int>(Attribute.AGE);
the method
public T getAttribute<T>(Attribute attribute){
{
Object result = attributeRepositoryDictionary[attribute];
return (T)result;
}
is this as closest as I can get with c#, where casting is needed for getting attributes?
I'm not sure I really like it as an idea - but you could do this by making Attribute generic:
public static class Attributes
{
public static Attribute<int> Age = new Attribute<int>("age");
public static Attribute<string> Name = new Attribute<string>("name");
}
public class Attribute<T>
{
public string Key { get; }
public Attribute(string key)
{
Key = key;
}
...
}
Then you can implement your method as:
public T GetAttribute<T>(Attribute<T> attribute)
{
// attributeDictionary would be a Dictionary<string, object>
return (T) attributeDictionary[attribute.Key];
}
At that point, type inference will be your friend, so you can just write:
int a = player1.GetAttribute(Attributes.Age);
and it'll be equivalent to:
int a = player1.GetAttribute<int>(Attributes.Age);
I was able to find an alternative solution along with #Jon Skeet, which this was more of what I was looking for (not sure if it is an ideal design though). Anyhow, little did I know there was a keyword called "dynamic" in cSharp.
It allowed my method getAttribute() to return any type at runtime, The only requirement is that you must know the return type.
So I do not recommend using this approach if you plan to work with many return types that are in the 5+. That's why I would recommend using something like an enum to give clues on what type will be returned.
In my case, I will only deal with the basic common return types (int, string, float, bool), so its rather easy to know what type will be returned base on the attribute that is called.
class Entity
{
Dictionary<Attribute, Object> attributeRepositoryEnumMap;
...
public dynamic getAttribute(Attribute attribute){
Object result = attributeRepositoryEnumMap[attribute];
return result;
}
}
now I can get return attributes like in the example below without casting
class MyApp
{
Entity e = new Entity();
e.setAttribute(Attribute.NAME, "Bob");
e.setAttribute(Attribute.AGE, 55);
e.setAttribute(Attrbute.HEIGHT, 5.5f);
string name = e.getAttribute(Attribute.NAME);
int age = e.getAttribute(Attribute.AGE);
float height = e.setAttribute(Attribute.HEIGHT);
}
Not sure how this will all work out for me, but a part of this attempt was to also find an easier work around saving and hydrating my json objects without creating tons of setters and getters.
I've received a working code (in Java, 1.7) that does the following:
load an array of strings (a list of blood test names) from a file into a string array member (using Properties and FileInputStream). The file can change the strings but the meaning stays the same (for example: a test can be called "abc" and in another run it is called "zzz"). I've got an enum class that enumerates the test names. The enum strings aren't the same as the inputted strings (since the latter can change).
file bloodtest.names contains:
bloodTestNames=abc;def;123;
code:
public enum BloodTestNames {
AAA,BBB,CCC;
}
Properties props = new Properties();
FileInputStream fis = new FileInputStream("bloodtest.names");
props.load(fis);
String testName[]=props.getProperty("bloodTestNames").toString().split(";");
Now to the questions:
Question 1:
I need to return the string that was set in the file when I know the test name (for instance: return "def" for value BBB). What's the best of doing that?
the best way I've come up with is:
return testName[BloodTestNames.BBB.ordinal()]
Question 2: if BBB is not known in compile time - how do I accomplish the same target?
Three points:
* I'm a veteran at C but a newbie with Java. Any Do's and Don't are welcome. Assume my Java knowledge is zero.
* I don't total re-factoring is that's what's needed here.
* I've probably forgot to mention important details, please ask and I'll feel the missing gaps
I'll first assume you do need enum constants for modeling this use-case because you have some sort of specific code to be executed for each kind of blood test (otherwise, a simple set of strings would be enough and more flexible, since you don't need to know the number of tests upfront or care about their names).
Q1: Since Java enums are a little more than a sequence of values, you can make full use of their object oriented nature.
public enum BloodTest {
AAA, BBB, CCC;
private static String[] names;
public static void setNames(String[] names) {
if (BloodTest.names != null)
throw new IllegalStateException("You can only set the names once");
if (names.length != values().length)
throw new IllegalArgumentException("Wrong number of names");
BloodTest.names = names;
}
#Override
public String toString() {
return names[ordinal()];
}
}
Now all you need to do is to initialize your enum by calling BloodTest.setNames(namesFromConfiguration) and then you can get the string representation of each constant by calling the standard toString() method on it: BloodTest.BBB.toString().
Since the initial assumption was that you have some specific logic for each of the test types, I would suggest that logic (as well as the required properties) will also be encapsulated in the enum itself or the enum constants; e.g.:
public enum BloodTest {
AAA(10) {
#Override
public boolean isRequired(MedicalRecord medicalRecord) {
return medicalRecord.includes("someDisease");
}
},
BBB(15) {
#Override
public boolean isRequired(MedicalRecord medicalRecord) {
return ! medicalRecord.hasTakenBloodTestsLately();
}
},
CCC(20) { // ... also implements the abstract method and so on
private final int threshold;
private BloodTest(int threshold) {
this.threshold = threshold;
}
public boolean hasPassed(int value) {
return value <= threshold;
}
public abstract boolean isRequired(MedicalRecord medicalRecord);
// ... same as above
}
Now, once you get a reference to some BloodTest, you can check whether that specific test passed by invoking the corresponding method without switching and having the logic spread around the client code:
BloodTest bloodTest = BloodTest.valueOf(someString); // someString can be "AAA", "BBB" or "CCC"
// no matter which constant this is, you use it as an object and rely on polymorphism
if (bloodTest.hasPassed(someValue)) { // ... do something
Q2: Your question 2 kind of "questions" my initial assumption regarding your actual need for an enum. If there's a chance you'll need to dynamically handle blood tests that you don't know about yet, then you can't use an enum.
In other words, if your code does not have any switch or if/else if blocks to handle each blood test, an enum is a really bad choice for your use case.
However, if it does, than I'd recommend refactoring the code to include the logic in the enum itself as in the above example, rather than in switch/if blocks; moreover, if your switch has a default case (or your if has a final else block), this can still be modeled in the enum itself, for instance by adding a DEFAULT constant as a fallback.
Make the whole thing settings driven: Add a statuc method to load in settings of what string maps to what enum and add a factory method that uses these settings:
public enum BloodTestNames {
AAA,BBB,CCC;
private static Map<String, BloodTestNames> map = new HashMap<String, BloodTestNames>();
public static void addAlias(String alias, String name) {
map.put(alias, valueOf(name));
}
public static BloodTestNames getByAluas(String alias) {
if (map.containsKey(alias))
return map.get(alias);
// own name assumed to be mapped
return valueOf(alias);
}
}
On startup, repeatedly call BloodTestNames.addAlias() based on some settings file to load the mappings.
When you're reading the saved file, use BloodTestNames.getByAlias() to return the enum for a given string value.
You would do well to name your class in the singular, and drop "Name", ie BloodTest - name the class for what each enum is (all enums have a "name" which is the coded instance name).
A short extract from one of my enum class :
public enum TypesStructurelsE {
SOURCE("SRC"),
COLONNE("COL");
private String code;
TypesStructurelsE(final String code1) {
code = code1;
}
/** #return String */
public String getCode() {
return code;
}
public void setCode(final String newCode) {
code = newCode;
}
}
. . In other class
if(TypesStructurelsE.SOURCE.getCode().equal(testName[i])){ // can be "COL" or "SRC"
//
;
}
... changing value :
TypesStructurelsE.SOURCE.setCode("SOURCE_NEW");
So, if your properties file change, you have just to compile with the new symbole (SRC --> SOURCE) no more
I'm trying to create a class that can instantiate arrays at runtime by giving each array a "name" created by the createtempobjectname() method. I'm having trouble making this program run. I would also like to see how I could access specific objects that were created during runtime and accessing those arrays by either changing value or accessing them. This is my mess so far, which compiles but gets a runtime exception.
import java.lang.reflect.Array;
public class arrays
{
private static String temp;
public static int name = 0;
public static Object o;
public static Class c;
public static void main(String... args)
{
assignobjectname();
//getclassname();//this is supposed to get the name of the object and somehow
//allow the arrays to become updated using more code?
}
public static void getclassname()
{
String s = c.getName();
System.out.println(s);
}
public static void assignobjectname()//this creates the object by the name returned
{ //createtempobjectname()
try
{
String object = createtempobjectname();
c = Class.forName(object);
o = Array.newInstance(c, 20);
}
catch (ClassNotFoundException exception)
{
exception.printStackTrace();
}
}
public static String createtempobjectname()
{
name++;
temp = Integer.toString(name);
return temp;
}
}
Create a Map then you can add key/value pairs when the key is your name and the value is your array.
Following up from #Ash's answer, here is some illustrative code. Notice that there is no reflection involved.
Map<String, Object> myMap = new HashMap<String, Object>();
...
Object myObject = ...
myMap.put("albert", myObject); // record something with name "albert"
...
Object someObject = myMap.get("albert"); // get the object named "albert"
// get("albert") would return null if there nothing with name "albert"
EDIT I've edited the example to use the type Object, since that is more closely aligned with what you are trying to do (I think). But you could use any type instead of Object ... just replace the type throughout the example. And you can do the same with an ArrayList; for example:
List<Date> dates = new ArrayList<Date>();
dates.add(new Date());
Date firstDate = dates.get(0);
Notice that no typecasts are required.
I expect you're getting a ClassNotFoundException from this line:
c = Class.forName(object);
The value of object the first time it's called is "1", which is not a valid class name.
Class.forName requires a class name as input, such as "java.lang.Integer". Trying to "name" your array in this way doesn't make sense to me. You need to pick an appropriate Java class name.
If you want to "name" an array instance (after you've created it), you could always store the instance as the value in a Map, using the name as the key.