A dictionary-like data structure. Is this a good practice?

I need a data structure to store various types of objects. For instance. String , Boolean and other classes.
Uses Map<String, Object> , where with the key you get the corresponding object, which suggests that you know how to distinguish it from good practice?
Is there a better solution?

+4
source share
2 answers

This is the perfect use case for PropretyHolder , which I wrote a while ago. You can read more about this on my blog . I designed it with the same thing in mind, feel free to adapt it to your needs.

In general, I would say if you want to profit from type safety in Java, you need to know your keys. What I mean by this is hardly possible to develop a secure solution such as where keys come from an external source.


Here is a special key that knows the type of its value (it is not completed, please download the source for the full version):

 public class PropertyKey<T> { private final Class<T> clazz; private final String name; public PropertyKey(Class<T> valueType, String name) { this.clazz = valueType; this.name = name; } public boolean checkType(Object value) { if (null == value) { return true; } return this.clazz.isAssignableFrom(value.getClass()); } ... rest of the class } 

Then you create a data structure that uses it:

 public class PropertyHolder { private final ImmutableMap<PropertyKey<?>, ?> storage; /** * Returns value for the key of the type extending-the-one-declared-in-the {@link PropertyKey}. * * @param key {@link PropertyKey} instance. * @return Value of the type declared in the key. */ @SuppressWarnings("unchecked") public <T extends Serializable> T get(PropertyKey<T> key) { return (T) storage.get(key); } /** * Adds key/value pair to the state and returns new * {@link PropertyHolder} with this state. * * @param key {@link PropertyKey} instance. * @param value Value of type specified in {@link PropertyKey}. * @return New {@link PropertyHolder} with updated state. */ public <T> PropertyHolder put(PropertyKey<T> key, T value) { Preconditions.checkNotNull(key, "PropertyKey cannot be null"); Preconditions.checkNotNull(value, "Value for key %s is null", key); Preconditions.checkArgument(key.checkType(value), "Property \"%s\" was given " + "value of a wrong type \"%s\"", key, value); // Creates ImmutableMap.Builder with new key/value pair. return new PropertyHolder(filterOutKey(key) .put(key, value).build()); } /** * Returns {@link Builder} with all the elements from the state except for the given ket. * * @param key The key to remove. * @return {@link Builder} for further processing. */ private <T> Builder<PropertyKey<? extends Serializable>, Serializable> filterOutKey(PropertyKey<T> key) { Builder<PropertyKey<? extends Serializable>, Serializable> builder = ImmutableMap .<PropertyKey<? extends Serializable>, Serializable> builder(); for (Entry<PropertyKey<? extends Serializable>, Serializable> entry : this.storage.entrySet()) { if (!entry.getKey().equals(key)) { builder.put(entry); } } return builder; } ... rest of the class } 

I will miss a lot of unnecessary details here, please let me know if something is unclear.

+5
source

A typesafe heterogeneous container can be used for this purpose:

 import java.util.HashMap; import java.util.Map; public class Container { private Map<Class<?>, Object> container = new HashMap<Class<?>, Object>(); public <T> void putElement(Class<T> type, T instance) { if (type == null) { throw new NullPointerException("Type is null"); } //container.put(type, instance); // 'v1' container.put(type, type.cast(instance)); // 'v2' runtime type safety! } public <T> T getElement(Class<T> type) { return type.cast(container.get(type)); } public static void main(String[] args) { Container myCont = new Container(); myCont.putElement(String.class, "aaa"); myCont.putElement(Boolean.class, true); myCont.putElement(String[].class, new String[] {"one", "two"}); System.out.println(myCont.getElement(String.class)); System.out.println(myCont.getElement(String[].class)[1]); } } 

Limitation: this container in its form is able to store only one instance / type of object.

In putElement() you can achieve runtime security using dynamic conversion. This will add extra overhead.

For example: try passing a class object to a container. Note where the exception occurs:

 Class raw = Class.forName("MyClass"); myCont.putElement(raw, "aaa"); //ClassCastException if using 'v2' System.out.println(myCont.getElement(raw)); //ClassCastException if using 'v1' 
+2
source

Source: https://habr.com/ru/post/1434152/


All Articles