This question already exists:
Custom Enum in Java
Closed 5 years ago.
I am trying to implement some kind of custom enum - a class which would act like enum and would implement its methods. I need to pass a set of values make them final and do operations like ordinal, valueOf, values.
Here is my implementation:
public class CustomEnum {
private static final Map<String,CustomEnum> valuesMap = new LinkedHashMap<>();
private static final List<CustomEnum> valuesList = new ArrayList<>();
public CustomEnum(String...data) {
for(String s : data){
final CustomEnum customEnum = new CustomEnum(s);
valuesMap.put(s, customEnum);
valuesList.add(customEnum);
}
}
public CustomEnum valueOf(final String data){
if (data == null) {
throw new NullPointerException();
}
final CustomEnum customEnum = valuesMap.get(data);
if(customEnum == null){
throw new IllegalArgumentException();
}
return customEnum;
}
public CustomEnum[] values(){
return valuesList.toArray(new CustomEnum[valuesList.size()]);
}
public int ordinal(){
return valuesList.indexOf(this);
}
}
And when I create an instance of a class I get StackOverflow error:
CustomEnum customEnum = new CustomEnum("white");
I understand why this error is happening, but I do not know how elese I can implement such class. The question is how I can change my implementation but still keep all the methods and data structures (arraylists, map) working?
I would be very grateful for some help.
Another solution would be a second constructor:
public CustomEnum(String s) {
valuesMap.put(s, this);
valuesList.add(this);
}
The one constructor you have is calling itself endlessly.
You problem is that the constructor as written is actually a factory. Move that functionality out of the constructor and you're good.
public static class CustomEnum {
private static final Map<String,CustomEnum> valuesMap = new LinkedHashMap<>();
private static final List<CustomEnum> valuesList = new ArrayList<>();
private final String data;
public CustomEnum(String data) {
this.data = data;
}
public CustomEnum valueOf(final String data){
if (data == null) {
throw new NullPointerException();
}
final CustomEnum customEnum = valuesMap.get(data);
if(customEnum == null){
throw new IllegalArgumentException();
}
return customEnum;
}
public CustomEnum[] values(){
return valuesList.toArray(new CustomEnum[valuesList.size()]);
}
public int ordinal(){
return valuesList.indexOf(this);
}
public static void create(String...data) {
for(String s : data){
final CustomEnum customEnum = new CustomEnum(s);
valuesMap.put(s, customEnum);
valuesList.add(customEnum);
}
}
}
public void test(String[] args) {
CustomEnum.create("white");
}
You have a design problem.
The public constructor values static fields.
So at each time a CustomEnum created, their content is overwritten.
So these collections don't have invariant elements :
private static final Map<String,CustomEnum> valuesMap = new LinkedHashMap<>();
private static final List<CustomEnum> valuesList = new ArrayList<>();
To solve your problem, you have two ways.
And in any cases, you have to make the constructor private to allow invariant and decouple the global construction that creates all CustomEnum instances from the creation of each one of them :
private CustomEnum(String data) {
this.data = data;
}
First way : Provide the constant values directly in the class (as enum does).
static{
String[] data = {....}; // constant values
for(String s : data){
final CustomEnum customEnum = new CustomEnum(s);
valuesMap.put(s, customEnum);
valuesList.add(customEnum);
}
}
Second way : make fields and methods not static and allows to create more than one CustomEnum with different values.
private final Map<String,CustomEnum> valuesMap = new LinkedHashMap<>();
private final List<CustomEnum> valuesList = new ArrayList<>();
public static void of(String... data) {
for(String d : data){
final CustomEnum customEnum = new CustomEnum(d);
valuesMap.put(d, customEnum);
valuesList.add(customEnum);
}
}
Related
I have 3 different Set act as a cache and each serves for different reason. Additionally the keys are of different types e.g. Integer, String etc
I was thinking to create a wrapper class around this, but then I thought of just having them all as part of 1 hashmap and based on the key I can pick the proper Set
But I am not sure what is the proper way to do that.
I'd like to avoid something like:
private final Map<Integer, Set<Object>> cache = new HashMap<>();
public boolean exists(Integer type, Object key) {
return cache.get(type).contains(key);
}
public void addKey(Integer type, Object key) {
if(type == CACHE_1) {
Set<Object> set = cache.get(type);
if(set == null) {
set = new HashSet<>();
cache.put(type, set);
}
set.add(key);
}
}
Is there a way to make it more type specific?
Update
These can be called as:
addKey(CACHE_1, "foo");
addKey(CACHE_2, 123);
or
if(exists(CACHE_1, "foo")
if(exists(CACHE_2, 123)
I am not sure you will like this, but you could use a "heterogeneous container". For example, define the Key of the Map:
static class Key<T> {
private final Integer one;
private final String two;
private final Long three;
private final Class<T> cls;
private Key(Integer one, String two, Long three, Class<T> cls) {
this.one = one;
this.two = two;
this.three = three;
this.cls = cls;
}
public Class<T> getCls() {
return cls;
}
public static Key<Integer> ofInteger(Integer one){
return new Key<>(one, null, null, Integer.class);
}
public static Key<String> ofString(String two){
return new Key<>(null, two, null, String.class);
}
public static Key<Long> ofLong(Long three){
return new Key<>(null, null, three, Long.class);
}
#Override
public int hashCode() {
return Objects.hash(one, two, three);
}
// simplified for example purpose
public boolean equals(Object obj) {
Key<?> other = (Key<?>)obj;
return Objects.equals(this.one, other.one) &&
Objects.equals(this.two, other.two) &&
Objects.equals(this.three, other.three);
}
}
Then define the container:
static class Holder {
private static final Map<Key<?>, Set<Object>> CACHE = new HashMap<>();
public static <T> boolean exists(Key<?> type, T key) {
return CACHE.get(type).contains(key);
}
public static <T> void addKey(Key<T> type, T key) {
CACHE.computeIfAbsent(type, x -> new HashSet<>()).add(key);
}
public static <T> Set<T> byKey(Key<T> key) {
Set<Object> set = CACHE.get(key);
return Optional.ofNullable(set).orElse(Collections.emptySet()).stream().map(key.getCls()::cast).collect(Collectors.toSet());
}
}
And some usage:
public static void main(String[] args) {
Holder.addKey(Key.ofInteger(1), 11);
Holder.addKey(Key.ofInteger(1), 22);
Holder.addKey(Key.ofInteger(1), 33);
Set<Integer> setI = Holder.byKey(Key.ofInteger(1));
Holder.addKey(Key.ofString("1"), "11");
Holder.addKey(Key.ofString("2"), "22");
Holder.addKey(Key.ofString("3"), "33");
Set<String> setS = Holder.byKey(Key.ofString("1"));
System.out.println(setI);
System.out.println(setS);
}
I'm using a Singleton to store a cache of objects but whenever I call the Singleton and add to the HashMap, it has no values.
The cache is checked at the beginning of a method (when the size is 1) but when adding to the HashMap again, the size of it is 0. The size of it therefore alternates between 0 and 1.
public class CachedObjects
{
static HashMap<String, Object> cachedObjects = new HashMap<>();
private static class InstanceHolder
{
private static final CachedObjects instance = new CachedObjects();
}
public static CachedObjects getInstance()
{
return CachedObjects.InstanceHolder.instance;
}
public void addObjectToCache(Object object)
{
cachedObjects.put(object.getTitle(), object);
}
public Object checkCacheForObject(String title)
{
Iterator it = cachedObjects.entrySet().iterator();
while (it.hasNext())
{
Map.Entry pair = (Map.Entry) it.next();
if (pair.getKey().equals(title))
{
return (Object) pair.getValue();
}
it.remove(); // avoids a ConcurrentModificationException
}
return null;
}
}
Where it's called:
public Object getObjectInfoFrom(String title)
{
Object cachedObjectCheck = CachedObjects.getInstance().checkCacheForObject(title);
// Size of HashMap is usually 1 here
if (cachedObjectCheck != null)
{
return cachedObjectCheck ;
}
// Lots of DB fetching here
Object object = new Object(DB details above);
CachedObjects.getInstance().addObjectToCache(object);
// The size of the HashMap always seems to be empty here
return object;
}
public class MyContext {
private static MyContext ourInstance = null;
private HashMap<String, String> translatedValue;
public static MyContext getInstance() {
if (ourInstance == null)
ourInstance = new MyContext();
return ourInstance;
}
private MyContext() {
translatedValue = new HashMap<>();
}
public void addTranslatedValue(String title, String value) {
translatedValue.put(title, value);
}
public String getTranslatedValue(String value) {
return translatedValue.get(value);
}
}
Using
MyContext.getInstance().addTranslatedValue("Next", valueTranslated);
System.out.println(myContext.getTranslatedValue("Next"));
Result
valueTranslated
First of all, this is not singleton because you have not hidden the constructor.
second, you need to remove this line:
it.remove(); // avoids a ConcurrentModificationException
Try this code, it works OK:
private static CachedObjectsClass singletonInstance = null;
HashMap<String, Object> cachedObjects;
private CachedObjectsClass()
{
cachedObjects = new HashMap<>();
}
public static CachedObjectsClass getInstance()
{
singletonInstance = singletonInstance == null ? new CachedObjectsClass()
: singletonInstance;
return singletonInstance;
}
public void addObjectToCache(String key, Object object)
{
cachedObjects.put(key, object);
}
public Object checkCacheForObject(String title)
{
return cachedObjects.get(title);
}
And usage:
Object cachedObjectCheck = CachedObjectsClass.getInstance()
.checkCacheForObject("kk");
CachedObjectsClass.getInstance().addObjectToCache("l", object);
public class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
}
public class SubClass
{
private ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
}
public class ComplexObject
{
List<CrazyObject> crazyObjects;
private final ObjectToProxy storedAsAField;
}
I have a complex object graph. Lets say it looks a little like the one above (even though it is much deeper in the real system). I would like, after being given ComplexObject, to be able to traverse the object graph and replace all ObjectToProxys with a proxying object.
Is this doable?
The reason for this is that we have some pretty big nasty objects which we partially load on the servers side (legacy, you're my friend!). We have a semi-working solution that uses proxying on the client side to go through and loads the full object when needed.
edit I would like to replace every instance of ObjectProxy connected to a ComplexObject.
public static class ProxyObject extends ObjectToProxy
{
private final ObjectToProxy objectToProxy;
public ProxyObject(ObjectToProxy objectToProxy)
{
this.objectToProxy = objectToProxy;
}
#Override
public String toString()
{
return "ProxyObject";
}
}
public static class ObjectToProxy
{
List<ObjectToProxy> potentiallyCircularReference;
public ObjectToProxy()
{
potentiallyCircularReference = new ArrayList<>();
potentiallyCircularReference.add(this);
}
#Override
public String toString()
{
return "ObjectToProxy";
}
}
public static class SubClass
{
ObjectToProxy aField;
Set<ObjectToProxy> aSetOfObjectsToProxy;
}
public static class CrazyObject
{
Map<Integer, ObjectToProxy> proxiedObjects;
List<SubClass> manySubClasses;
public CrazyObject()
{
proxiedObjects = new HashMap<>();
proxiedObjects.put(1, new ObjectToProxy());
}
}
public static class ComplexObject
{
List<CrazyObject> crazyObjects;
final ObjectToProxy storedAsAField;
public ComplexObject()
{
this.storedAsAField = new ObjectToProxy();
crazyObjects = new ArrayList<>();
crazyObjects.add(new CrazyObject());
}
#Override
public String toString()
{
return "myField: " + storedAsAField.toString();
}
}
public static void main(String[] args) throws Exception
{
ComplexObject obj = new ComplexObject();
Set<Object> visitedObjects = Sets.newIdentityHashSet();
Queue<Object> objectsToVisit = new LinkedList<>();
visitedObjects.add(obj);
objectsToVisit.add(obj);
while (!objectsToVisit.isEmpty())
{
handleFields(objectsToVisit.poll(), visitedObjects, objectsToVisit);
}
System.out.println(obj.toString());
}
private static void handleFields(Object obj, Set<Object> visitedObjects, Queue<Object> objectsToVisit) throws Exception
{
List<Field> fields = getAllFields(obj);
for (Field field : fields)
{
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue != null && !visitedObjects.contains(fieldValue))
{
if (fieldValue instanceof Object[])
{
visitedObjects.add(fieldValue);
Object[] array = (Object[])fieldValue;
for (Object arrayObj : array)
{
if (arrayObj != null && !objectsToVisit.contains(arrayObj))
{
visitedObjects.add(arrayObj);
if (!DontLookAt.contains(arrayObj.getClass()))
objectsToVisit.add(arrayObj);
}
}
}
else
{
if (!DontLookAt.contains(fieldValue.getClass()))
objectsToVisit.add(fieldValue);
}
if (fieldValue.getClass().equals(ObjectToProxy.class))
{
field.set(obj, new ProxyObject((ObjectToProxy)fieldValue));
}
else if (fieldValue instanceof ObjectToProxy[])
{
ObjectToProxy[] array = (ObjectToProxy[])fieldValue;
for (int i = 0; i < array.length; i++)
{
if (array[i] != null)
array[i] = new ProxyObject(array[i]);
}
}
}
}
}
private static final Set<Class> DontLookAt = getDontLookAtSet();
private static Set<Class> getDontLookAtSet()
{
Set<Class> set = new HashSet<>();
set.add(Long.class);
set.add(Boolean.class);
set.add(Integer.class);
set.add(String.class);
set.add(Byte.class);
set.add(Double.class);
set.add(Float.class);
set.add(Class.class);
return set;
}
private static List<Field> getAllFields(Object obj) throws Exception
{
List<Field> output = new ArrayList<>();
Class klazz = obj.getClass();
while (!klazz.equals(Object.class))
{
Field[] fields = klazz.getDeclaredFields();
output.addAll(Arrays.asList(fields));
klazz = klazz.getSuperclass();
}
return output;
}
For anyone wondering, The above simulates and does what I'm after. I'm sure it isn't perfect, but it is good enough for my purposes.
Needing to create an unspecified number of objects, I tried to create a builder that do that. All was well until I realized that my builder creates all objects with their properties having the same values.
So when I call the builder:
ValidationHelper v = new ValidationHelper.HelperBuilder()
.addHelper("ICAO Identifier", icaoIdentifier, rulesICAO)
.addHelper("Long Name", longName, rulesLongName)
.build();
... I'll have 2 objects and their properties will have values of the last object the builder was asked to create.
To start with, is factory builder the prudent approach to this? Secondly, is my builder salvageable?
Builder:
public class ValidationHelper {
private static ArrayList<HelperBuilder> validatorHelpers = new ArrayList();
public static class HelperBuilder {
private String txtFieldName;
private String txtFieldValue;
private List<Integer> valCodes = new ArrayList<Integer>();
private ArrayList<HelperBuilder> innerValidatorHelpers = new ArrayList<HelperBuilder>();
public HelperBuilder() {}
public final HelperBuilder addHelper(String txtFieldName, String txtFieldValue, int[] validationCodes) {
this.txtFieldName = txtFieldName;
this.txtFieldValue = txtFieldValue;
for( int i = 0; i < validationCodes.length; i++ ){
getValCodes().add((Integer) validationCodes[i]);
}
innerValidatorHelpers.add(this);
return this;
}
public final ValidationHelper build() {
return new ValidationHelper(this);
}
public String getTxtFieldName() {
return txtFieldName;
}
public String getTxtFieldValue() {
return txtFieldValue;
}
public List<Integer> getValCodes() {
return valCodes;
}
}//end HelperBuilder
private ValidationHelper(HelperBuilder helperBuilder) {
validatorHelpers = helperBuilder.innerValidatorHelpers;
}
public void setHelpers(ArrayList validatorHelpers) {
validatorHelpers = validatorHelpers;
}
public ArrayList getHelpers() {
return validatorHelpers;
}
}
EDIT/FIXED:
So for what it's worth, here's the revised builder. It needed another constructor that could properly initialize an instance of what it's supposed to build.
public class ValidationHelper {
private static ArrayList<HelperBuilder> validatorHelpers = new ArrayList();
public static class HelperBuilder {
private String txtFieldName;
private String txtFieldValue;
private List<Integer> valCodes = new ArrayList<Integer>();
private ArrayList<HelperBuilder> innerValidatorHelpers = new ArrayList<HelperBuilder>();
public HelperBuilder() {}
public HelperBuilder(String txtFieldName, String txtFieldValue, int[] validationCodes) {
this.txtFieldName = txtFieldName;
this.txtFieldValue = txtFieldValue;
for (int i = 0; i < validationCodes.length; i++) {
valCodes.add((Integer) validationCodes[i]);
}
}
public final HelperBuilder addHelper(String txtFieldName, String txtFieldValue, int[] validationCodes) {
innerValidatorHelpers.add( new HelperBuilder(txtFieldName, txtFieldValue, validationCodes) );
return this;
}
public final ValidationHelper build() {
return new ValidationHelper(this);
}
public String getTxtFieldName() {
return txtFieldName;
}
public String getTxtFieldValue() {
return txtFieldValue;
}
public List getValCodes() {
return valCodes;
}
}//end HelperBuilder
private ValidationHelper(HelperBuilder helperBuilder) {
validatorHelpers = helperBuilder.innerValidatorHelpers;
}
public ArrayList getHelpers() {
return validatorHelpers;
}
}
Each time you just overwrite the values in
private String txtFieldName;
private String txtFieldValue;
and the last one winns. So you create only 1 HelperInstance here
ValidationHelper v = new ValidationHelper.HelperBuilder()
and the fields name and value are overwritten each time you call addHelper(). But you need to create an instance for each "configuration". So addHelper should create a new Instance and add it into
private ArrayList<HelperBuilder> innerValidatorHelpers = ...;
If you want to build objects with different values you have to either
alter the builder between creating the objects so it will build something different.
instruct the builder to change the values automatically e.g. use a counter, or filename based on the date, or provide a list of values.
Given the following multiton:
public class Multiton
{
private static final Multiton[] instances = new Multiton[...];
private Multiton(...)
{
//...
}
public static Multiton getInstance(int which)
{
if(instances[which] == null)
{
instances[which] = new Multiton(...);
}
return instances[which];
}
}
How can we keep it thread safe and lazy without the expensive synchronization of the getInstance() method and the controversy of double-checked locking? An effective way for singletons is mentioned here but that doesn't seem to extend to multitons.
UPDATE: with Java 8, it can be even simpler:
public class Multiton {
private static final ConcurrentMap<String, Multiton> multitons = new ConcurrentHashMap<>();
private final String key;
private Multiton(String key) { this.key = key; }
public static Multiton getInstance(final String key) {
return multitons.computeIfAbsent(key, Multiton::new);
}
}
Mmm that's good!
ORIGINAL ANSWER
This is a solution which builds on the Memoizer pattern as described in JCiP. It uses a ConcurrentHashMap like one of the other answers, but instead of storing the Multiton instances directly, which can lead to creating unused instances, it stores the computation that leads to the creation of the Multiton. That additional layer solves the problem of unused instances.
public class Multiton {
private static final ConcurrentMap<Integer, Future<Multiton>> multitons = new ConcurrentHashMap<>();
private static final Callable<Multiton> creator = new Callable<Multiton>() {
public Multiton call() { return new Multiton(); }
};
private Multiton(Strnig key) {}
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
Future<Multiton> f = multitons.get(key);
if (f == null) {
FutureTask<Multiton> ft = new FutureTask<>(creator);
f = multitons.putIfAbsent(key, ft);
if (f == null) {
f = ft;
ft.run();
}
}
return f.get();
}
}
This will provide you a threadsafe storage mechanism for your Multitons. The only downside is that it is possible to create a Multiton that will not be used in the putIfAbsent() call. The possibility is small but it does exist. Of course on the remote chance it does happen, it still causes no harm.
On the plus side, there is no preallocation or initialization required and no predefined size restrictions.
private static ConcurrentHashMap<Integer, Multiton> instances = new ConcurrentHashMap<Integer, Multiton>();
public static Multiton getInstance(int which)
{
Multiton result = instances.get(which);
if (result == null)
{
Multiton m = new Multiton(...);
result = instances.putIfAbsent(which, m);
if (result == null)
result = m;
}
return result;
}
You could use an array of locks, to at least be able to get different instances concurrently:
private static final Multiton[] instances = new Multiton[...];
private static final Object[] locks = new Object[instances.length];
static {
for (int i = 0; i < locks.length; i++) {
locks[i] = new Object();
}
}
private Multiton(...) {
//...
}
public static Multiton getInstance(int which) {
synchronized(locks[which]) {
if(instances[which] == null) {
instances[which] = new Multiton(...);
}
return instances[which];
}
}
With the advent of Java 8 and some improvements in ConcurrentMap and lambdas it is now possible to implement a Multiton (and probably even a Singleton) in a much tidier fashion:
public class Multiton {
// Map from the index to the item.
private static final ConcurrentMap<Integer, Multiton> multitons = new ConcurrentHashMap<>();
private Multiton() {
// Possibly heavy construction.
}
// Get the instance associated with the specified key.
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
// Already made?
Multiton m = multitons.get(key);
if (m == null) {
// Put it in - only create if still necessary.
m = multitons.computeIfAbsent(key, k -> new Multiton());
}
return m;
}
}
I suspect - although it would make me feel uncomfortable - that getInstance could be further minimised to:
// Get the instance associated with the specified key.
public static Multiton getInstance(final Integer key) throws InterruptedException, ExecutionException {
// Put it in - only create if still necessary.
return multitons.computeIfAbsent(key, k -> new Multiton());
}
You're looking for an AtomicReferenceArray.
public class Multiton {
private static final AtomicReferenceArray<Multiton> instances = new AtomicReferenceArray<Multiton>(1000);
private Multiton() {
}
public static Multiton getInstance(int which) {
// One there already?
Multiton it = instances.get(which);
if (it == null) {
// Lazy make.
Multiton newIt = new Multiton();
// Successful put?
if ( instances.compareAndSet(which, null, newIt) ) {
// Yes!
it = newIt;
} else {
// One appeared as if by magic (another thread got there first).
it = instances.get(which);
}
}
return it;
}
}