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;
}
}
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);
}
My old code create a FrameBody subclass based on identifier
Class<AbstractID3v2FrameBody> c = (Class<AbstractID3v2FrameBody> Class.forName("org.jaudiotagger.tag.id3.framebody.FrameBody" + identifier);
Class<?>[] constructorParameterTypes = {Class.forName("java.nio.ByteBuffer"), Integer.TYPE};
Object[] constructorParameterValues = {byteBuffer, frameSize};
Constructor<AbstractID3v2FrameBody> construct = c.getConstructor(constructorParameterTypes);
frameBody = (construct.newInstance(constructorParameterValues));
But profile analysis shows its a bit slow, the alternative to using reflecting is a big switch statement
switch(identifier)
{
case ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION:
frameBody = new FrameBodyAENC(byteBuffer, frameSize);
break;
case ID3v24Frames.FRAME_ID_ATTACHED_PICTURE:
frameBody = new FrameBodyAPIC(byteBuffer, frameSize);
break;
.......
}
but I have over 100 identifiers, so its seems a bit cumbersome
Is there a more elegant way to do this in Java 8 ?
EDIT:
This can be simplified indeed as Holger suggests:
Map<String, BiFunction<ByteBuffer, Integer, AbstractID3v2FrameBody>> LOOKUP = Map.of(
FRAME_ID_AUDIO_ENCRYPTION, FrameBodyAENC::new,
FRAME_ID_ATTACHED_PICTURE, FrameBodyAPIC::new
);
And then as simple as:
LOOKUP.get(ID3v24Frames.FRAME_ID_AUDIO_ENCRYPTION)
.apply(byteBuffer, frameSize);
PREVIOUS SUGGESTION
This is just a sample, you will need to adapt it to your classes:
private static final Lookup L = MethodHandles.lookup();
private static final MethodHandle CONS;
static {
try {
CONS = L.findConstructor(SomeClass.class, MethodType.methodType(void.class, int.class));
} catch (Throwable t) {
throw new RuntimeException(t);
}
}
private static final Map<String, MethodHandle> LOOK_UP = Map.of(
"SOME_CLASS", CONS
);
public static void main(String[] args) {
try {
SomeClass sc = (SomeClass) LOOK_UP.get("SOME_CLASS").invokeExact(42);
System.out.println(sc.getX());
} catch (Throwable t) {
t.printStackTrace();
}
}
static class SomeClass {
private final int x;
public SomeClass(int x) {
this.x = x;
}
public int getX() {
return x;
}
}
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);
}
}
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);
When i was reviewing Builder pattern in Josh's Bloch book, i came up with simpler implementation, but i'm not sure whether it's proper.
For example:
public class Test {
public static void main(String[] args) {
Numbers first = new Numbers.Builder().setD(3.14).build();
System.out.println(first);
Numbers second = new Numbers.Builder().setI(17).setF(1.24F).build();
System.out.println(second);
System.out.println(first);
}
}
final class Numbers {
private int i;
private long l;
private float f;
private double d;
private Numbers() {}
public static class Builder {
private final Numbers instance = new Numbers();
public Builder setI(int i) {
instance.i = i;
return this;
}
public Builder setL(long l) {
instance.l = l;
return this;
}
public Builder setF(float f) {
instance.f = f;
return this;
}
public Builder setD(double d) {
instance.d = d;
return this;
}
public Numbers build() {
return instance;
}
}
#Override
public String toString() {
return String.format("%4d %4d %7.3f %7.3f", i, l, f, d);
}
}
Is it can still be considered as a Builder pattern or i missed something?
EDIT
What about this?
//...
private Numbers() {}
private Numbers(Numbers o) {
i = o.i;
l = o.l;
f = o.f;
d = o.d;
}
public static class Builder {
private final Numbers instance = new Numbers();
//...
public Numbers build() {
return new Numbers(instance);
}
}
The problem with your code is that if you call build twice for the same Builder instance, you'll get the same Numbers instance. And if you call methods of the Builder after you called build and got the Numbers instance, you will change that instance. The instance created by the builder should be independent of the Builder once it's built.