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);
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);
}
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 often have to deal with DTOs that contains other DTOs and I'd like to scan one object's attributes (and their own attributes, recursively) and retrieve every accessible object of class Bingo in the whole hierarchy.
For example, when I have the following :
public static class Bingo {
// the one I want to get
}
public static class Foo {
private Bar bar;
private Bingo bingo;
private List<Bingo> bingos;
// getters & setters
}
public static class Bar {
private Bingo bingo;
// getters & setters
}
I'd like to get all instances of Bingo found in attributes of my Foo object, including the ones in the Bar object and the List.
Is there a library conveniently doing that ?
A more complete test case (using a bit of JUnit) :
public static class Bingo {
private final int id;
public Bingo(int in_id) {
id = in_id;
}
#Override
public String toString() {
return "Bingo#"+String.valueOf(id);
}
}
public static class BingoWrapper {
private Bingo bingo;
public Bingo getBingo() {
return bingo;
}
public void setBingo(Bingo in_bingo) {
bingo = in_bingo;
}
}
public static class BingoFactory {
private final List<Bingo> ALL_BINGOS = new ArrayList<>();
private int sequence = 0;
public Bingo createBingo(){
Bingo l_bingo = new Bingo(sequence++);
ALL_BINGOS.add(l_bingo);
return l_bingo;
}
public BingoWrapper createBingoWrapper(){
BingoWrapper l_bar = new BingoWrapper();
l_bar.setBingo(createBingo());
return l_bar;
}
public List<Bingo> getAllBingos(){
return ALL_BINGOS.stream().collect(Collectors.toList());
}
}
public static class Foo {
private Bingo bingo;
private BingoWrapper wrapper;
private Bingo[] array;
private Collection<Object> collection;
private Map<Object,Object> map;
public Bingo getBingo() {
return bingo;
}
public void setBingo(Bingo in_bingo) {
bingo = in_bingo;
}
public BingoWrapper getWrapper() {
return wrapper;
}
public void setWrapper(BingoWrapper in_bar) {
wrapper = in_bar;
}
public Bingo[] getArray() {
return array;
}
public void setArray(Bingo[] in_array) {
array = in_array;
}
public Collection<Object> getCollection() {
return collection;
}
public void setCollection(Collection<Object> in_collection) {
collection = in_collection;
}
public Map<Object, Object> getMap() {
return map;
}
public void setMap(Map<Object, Object> in_map) {
map = in_map;
}
}
#Test
public void test(){
BingoFactory l_bingoFactory = new BingoFactory();
Foo l_foo = new Foo();
l_foo.setBingo(l_bingoFactory.createBingo()); // one in a field
l_foo.setWrapper(l_bingoFactory.createBingoWrapper()); // one in a field of a field
l_foo.setArray(new Bingo[]{l_bingoFactory.createBingo()}); // one in an array in a field
l_foo.setCollection(Arrays.asList(
l_bingoFactory.createBingo(), // one in Collection in a field
l_bingoFactory.createBingoWrapper())); // one in a field of an item in a Collection in a field
Map<Object,Object> l_map = new HashMap<>();
l_foo.setMap(l_map);
l_map.put("key", l_bingoFactory.createBingo()); // one as a key in a Map in a field
l_map.put(l_bingoFactory.createBingo(), "value"); // one as a value in a Map in a field
l_map.put("keyAgain", l_bingoFactory.createBingoWrapper()); // one wrapped in a value in a Map in a Field
l_map.put(l_bingoFactory.createBingoWrapper(), "valueAgain"); // one wrapped in a key in a Map in a field
List<Bingo> l_found = BeanUtils.scanObjectForType(l_foo, Bingo.class); // Magic happens here
System.out.println(l_found); // for debug
Assert.assertTrue(l_found.containsAll(l_bingoFactory.getAllBingos())); // I want them ALL
}
A solution with Spring's BeanUtils : (I've added a boolean to decide whereas objects of input class needed to be scanned or not. (i.e. do you expect your Bingo objects to contain other objects of type Bingo ?))
public static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType){
return scanObjectForType(in_object, in_type, in_scanSameType, new HashSet<>());
}
private static <T> List<T> scanObjectForType(Object in_object, Class<T> in_type, boolean in_scanSameType, Set<Object> in_alreadyScanned){
if(in_type == null){
throw new IllegalArgumentException("in_type should not be null");
}
if(in_object instanceof Class){
throw new IllegalArgumentException("in_type should not be a Class");
}
if(in_object == null || in_alreadyScanned.contains(in_object)){
return Collections.emptyList();
}
in_alreadyScanned.add(in_object); // to prevent infinite loop when inner object references outer object
if(in_type.isInstance(in_object)){
return Collections.singletonList((T) in_object);
}
List<T> l_result = new ArrayList<>();
if(in_type.isInstance(in_object)){
l_result.add((T) in_object);
if(!in_scanSameType){
return l_result;
}
}
if(in_object instanceof Object[]){
for(Object l_item : (Object[]) in_object){
l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned));
}
} else if(in_object instanceof Collection){
for(Object l_item : (Collection<Object>) in_object){
l_result.addAll(scanObjectForType(l_item, in_type, in_scanSameType, in_alreadyScanned));
}
} else if(in_object instanceof Map){
Map<Object,Object> l_map = (Map<Object,Object>) in_object;
for(Map.Entry<Object, Object> l_entry : l_map.entrySet()){
l_result.addAll(scanObjectForType(l_entry.getKey(), in_type, in_scanSameType, in_alreadyScanned));
l_result.addAll(scanObjectForType(l_entry.getValue(), in_type, in_scanSameType, in_alreadyScanned));
}
} else {
PropertyDescriptor[] l_descriptors = org.springframework.beans.BeanUtils.getPropertyDescriptors(in_object.getClass());
for(PropertyDescriptor l_descriptor : l_descriptors){
Method l_readMethod = l_descriptor.getReadMethod();
if(l_readMethod != null){
try {
Object l_readObject = l_readMethod.invoke(in_object);
if(l_readObject != null
&& !l_readObject.equals(in_object) // prevents infinite loops
&& !(l_readObject instanceof Class)){ // prevents weird loops when accessing properties of classes
l_result.addAll(scanObjectForType(l_readObject,in_type, in_scanSameType, in_alreadyScanned));
}
} catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
// too bad but continue
LOGGER.warn("Got an error trying to access field : ", e);
continue;
}
}
}
}
return l_result;
}
Its limitations :
Only scan properties with public accessors
Does not scan Class types (to prevent scanning of the whole ClassLoader's classes, and because the use-case is DTO-oriented).
Relies on recursivity. I guess it might be prettier to implement a BeanVisitor object that operates on a loop over a Set of nested beans.
Will scan Objects returned by getter methods that may not be properties.
It's not tested with inheritence.
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.
I have a POJO, and a (currently not-yet-built) class that will return Lists of it. I'd like to automatically generate the code necessary for the POJO to be accessed as a Map. Is this a good idea, is it possible to do automatically, and do I need to do this manually for every POJO I want to treat this way?
Thanks,
Andy
You can use Commons BeanUtils BeanMap for this.
Map map = new BeanMap(someBean);
Update: since that's not an option due to some apparent library dependency problems in Android, here's a basic kickoff example how you could do it with little help of Reflection API:
public static Map<String, Object> mapProperties(Object bean) throws Exception {
Map<String, Object> properties = new HashMap<>();
for (Method method : bean.getClass().getDeclaredMethods()) {
if (Modifier.isPublic(method.getModifiers())
&& method.getParameterTypes().length == 0
&& method.getReturnType() != void.class
&& method.getName().matches("^(get|is).+")
) {
String name = method.getName().replaceAll("^(get|is)", "");
name = Character.toLowerCase(name.charAt(0)) + (name.length() > 1 ? name.substring(1) : "");
Object value = method.invoke(bean);
properties.put(name, value);
}
}
return properties;
}
If java.beans API were available, then you could just do:
public static Map<String, Object> mapProperties(Object bean) throws Exception {
Map<String, Object> properties = new HashMap<>();
for (PropertyDescriptor property : Introspector.getBeanInfo(bean.getClass()).getPropertyDescriptors()) {
String name = property.getName();
Object value = property.getReadMethod().invoke(bean);
properties.put(name, value);
}
return properties;
}
Here's my own implementation without any dependencies. Rather than make a copy of the object's state, it implements a live Map over the pojo. Android doesn't support java.beans, but you can use openbeans instead.
import java.beans.*; // Or, import com.googlecode.openbeans.*
import java.util.*;
public class BeanMap extends AbstractMap<String, Object> {
private static final Object[] NO_ARGS = new Object[] {};
private HashMap<String, PropertyDescriptor> properties;
private Object bean;
public BeanMap(Object bean) throws IntrospectionException {
this.bean = bean;
properties = new HashMap<String, PropertyDescriptor>();
BeanInfo info = Introspector.getBeanInfo(bean.getClass());
for(PropertyDescriptor property : info.getPropertyDescriptors()) {
properties.put(property.getName(), property);
}
}
#Override public Object get(Object key) {
PropertyDescriptor property = properties.get(key);
if(property == null)
return null;
try {
return property.getReadMethod().invoke(bean, NO_ARGS);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override public Object put(String key, Object value) {
PropertyDescriptor property = properties.get(key);
try {
return property.getWriteMethod().invoke(bean, new Object[] {value});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override public Set<Map.Entry<String, Object>> entrySet() {
HashSet<Map.Entry<String, Object>> result = new HashSet<Map.Entry<String, Object>>(properties.size() * 2);
for(PropertyDescriptor property : properties.values()) {
String key = property.getName();
Object value;
try {
value = property.getReadMethod().invoke(bean, NO_ARGS);
} catch (Exception e) {
throw new RuntimeException(e);
}
result.add(new PropertyEntry(key, value));
}
return Collections.unmodifiableSet(result);
}
#Override public int size() { return properties.size(); }
#Override public boolean containsKey(Object key) {
return properties.containsKey(key);
}
class PropertyEntry extends AbstractMap.SimpleEntry<String, Object> {
PropertyEntry(String key, Object value) {
super(key, value);
}
#Override public Object setValue(Object value) {
super.setValue(value);
return BeanMap.this.put(getKey(), value);
}
}
}