I often see lists of objects in java holding beans whose objects are picked by inspecting an ID field, i.e.
List<BeanObj> list = …
BeanObj myObj = null;
for(BeanObj b : list)
if(b.getId().equals(whatIAmLookingFor)){
myObj = b;
break;
}
(The second variant of this is storing the objects in Hibernate and retrieve them by SQL.)
Using a Map interface would really be sensible here, but there are difficulties, i.e.
the key field may be changed (in general, or even concurrently)
the key may be non-trivial to reach (think of b.getRoot().getAttribute("id").equals(…)
Have there been approaches to address this in a more efficient way, like implementing a
SpecialMap<String, BeanObj>("id") // use String getId() on BeanObj
or even
SpecialMap<String, BeanObj>("getRoot().getAttribute({0})", "id")
// use String getAttribute("id") on result of getRoot()
with add() instead put() which makes use of the id getter function to build its internal map? Probably this map will require the mapped objects to implement some interface to allow the map being notified of updates on the id field.
Perhaps the map could also take care that changing the ID of an object to an ID of an existing object is either not possible or results in dropping the object that previously had that ID.
You can manage the functionnal aspect of adding element to your map by using guava utilities:
import com.google.common.base.Function;
public class SpecialMap<K, V> extends HashMap<K, V>{
private Function<V, K> function;
public SpecialMap(Function<V, K> function) {
this.function = function;
}
public void add(V value) {
K key = function.apply(value);
this.put(key, value);
}
public static void main(String[] args) {
SpecialMap<String, BeanObj> specialMap = new SpecialMap<String, BeanObj>(new Function<BeanObj, String>() {
#Override
public String apply(BeanObj arg) {
return arg.getRoot().getAttribute("id");
}
});
specialMap.add(new BeanObj());
}
}
In this example, the function will map your bean type to a string key.
Related
I have an interface which has map-like functionality, but does not implement Java's Map interface.
The map interface also implements Iterable<Object>; it iterates over the keys of the map
I'd like to use this in the body of an enhanced loop (see below), but without an assertion, and use get to retrieve values for the iterated keys, and without an [ERROR] from the Checker Framework.
Is that at all possible and could you provide pointers where to start or examples to learn from? I tried haphazardly to sprinkle some #KeyFors here and there, but with a lack of fully understanding what I'm doing it could take a while before I hit the right spots ;-)
I understand we might use an "Entry Iterator" and avoid to have to solve this problem in the first place, but I'm really just interested in learning how to teach the Checker Framework about the semantic relation between a key iterator and an #Nullable get method.
Here's a minimal working example:
import org.checkerframework.checker.nullness.qual.Nullable;
interface IMap extends Iterable<Object> {
#Nullable Object get(Object o);
IMap put(Object key, Object value); // immutable put
IMap empty();
default IMap remove(Object key) {
IMap tmp = empty();
for (Object k : this) {
if (!k.equals(key)) {
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
}
}
return tmp;
}
}
class Map implements IMap {
java.util.Map<Object, Object> contents = new java.util.HashMap<>();
public Map() { }
private Map(java.util.Map<Object, Object> contents) {
this.contents = contents;
}
#Override
public #Nullable Object get(Object key) {
return contents.get(key);
}
#Override
public IMap empty() {
return new Map();
}
#Override
public IMap put(Object key, Object value) {
java.util.Map<Object, Object> newContents = new java.util.HashMap<>();
newContents.putAll(contents);
newContents.put(key, value);
return new Map(newContents);
}
#Override
public java.util.Iterator<Object> iterator() {
return contents.keySet().iterator();
}
}
The Nullness Checker is warning you that the specifications (the type annotations) are inconsistent with the code itself.
Nullness problem
The key problem with your code is here:
tmp.put(k, get(k))
and the error message is:
error: [argument.type.incompatible] incompatible types in argument.
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
^
found : #Initialized #Nullable Object
required: #Initialized #NonNull Object
Here are the two specifications that are incompatible:
put requires a non-null second argument (recall that #NonNull is the default):
public IMap put(Object key, Object value) { ... }
get might return null at any time, and clients have no way to know when the return value might be non-null:
#Nullable Object get(Object o);
If you want to state that the return value of a method is nullable in general, but is non-null in certain situations, then you need to use a conditional postcondition such as #EnsuresNonNullIf.
That said, the Nullness Checker has special handling for Map.get. Your code doesn't use that, because you don't have a method that overrides java.util.Map.get (though it does have a class named Map that has nothing to do with java.util.Map).
If you want special-case handling for IMap.get, then either:
your class should extend java.util.Map, or
you should extend the Nullness Checker to recognize your class.
Map key problem
could you provide pointers where to start or examples to learn from?
I suggest starting with the Checker Framework Manual. It has lots of explanations and examples. You should read at least the Map Key Checker chapter. It links to further documentation, such as Javadoc for #KeyFor.
I tried haphazardly to sprinkle some #KeyFors here and there, but with a lack of fully understanding what I'm doing it could take a while before I hit the right spots ;-)
Please don't do that! That way lies suffering. The manual tells you not to do that; instead, think first and write specifications that describe your code.
Here are three #KeyFor annotations that you culd write:
interface IMap extends Iterable<#KeyFor("this") Object> {
...
default IMap remove(#KeyFor("this") Object key) {
...
#SuppressWarnings("keyfor") // a key for `contents` is a key for this object
public java.util.Iterator<#KeyFor("this") Object> iterator() {
These annotations state, respectively:
The iterator returns keys for this object.
Clients must pass a key for this object.
The iterator returns keys for this object. I suppressed a warning because this object acts as a wrapper for a contained object, and I don't recall that the Checker Framework has a way to say, "This object is a wrapper around a field and each of its methods has the same properties as the methods of that field."
The result type-checks without problems (except the nullness one noted in the first section of this answer):
import org.checkerframework.checker.nullness.qual.KeyFor;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
interface IMap extends Iterable<#KeyFor("this") Object> {
#Nullable Object get(Object o);
IMap put(Object key, Object value); // immutable put
IMap empty();
default IMap remove(#KeyFor("this") Object key) {
IMap tmp = empty();
for (Object k : this) {
if (!k.equals(key)) {
tmp.put(k, get(k)); // get(k) is always non-null because of the key iterator
}
}
return tmp;
}
}
class Map implements IMap {
java.util.Map<Object, Object> contents = new java.util.HashMap<>();
public Map() {}
private Map(java.util.Map<Object, Object> contents) {
this.contents = contents;
}
#Override
public #Nullable Object get(Object key) {
return contents.get(key);
}
#Override
public IMap empty() {
return new Map();
}
#Override
public IMap put(Object key, Object value) {
java.util.Map<Object, Object> newContents = new java.util.HashMap<>();
newContents.putAll(contents);
newContents.put(key, value);
return new Map(newContents);
}
#Override
#SuppressWarnings("keyfor") // a key for `contents` is a key for this object
public java.util.Iterator<#KeyFor("this") Object> iterator() {
return contents.keySet().iterator();
}
}
To summarize the informative accepted answer:
There is no way to annotate the given code example such that the semantic relation between the iterator and the get method of IMap can be specified to the Checker Framework;
As a result the current reported error requires an local non-nullness assert, a rewrite of the code which avoids a key iterator or a SuppressWarning annotation.
An extension to the checker framework would be necessary, among the lines of how it was special-cased for java.util.Map, if we want to avoid these workarounds.
I have an interface called InterA which has a method call operate() that has its return value as Result.
public interface InterA<T>
{
Result operate(T source);
}
Assume Class MachineOperator and Class CarOperator are implementations of the above with two T object types.
public class MachineOperator implements InterA<Machine>
{
Result operate(Machine source)
{
//Do some operation here and return result.
}
}
public class CarOperator implements InterA<Car>
{
Result operate(Car source)
{
//Do some operation here and return result.
}
}
And I want to keep these implementations in a map so that when I give the class I can have the implementation. So my map would be like this.
Map<Class<?>, InterA<T>> map = new HashMap<>();
And when I go to fill the map I have to do following,
map.put(Machine.class, (InterA<T>) new MachineOperator());
map.put(Car.class, (InterA<T>) new CarOperator());
Is there any way of defining the map so there is no longer a need to cast when adding?
=============================================================
I have tried the following also which can avoid casting when adding to map.
public interface InterA
{
Result <T> operate(T source);
}
Then in each implementation I have to have additional casting for each T.
public class MachineOperator implements InterA
{
< Machine> Result operate(Machine source)
{
(Machine) source; //Need to cast for Machine In-order to read attributes.
//Do some operation here and return result.
}
}
Out of these what would be the correct approach? Is there any different solution?
You could write a wrapper for your Map to allow adding only corresponding InterA objects:
class ClassMap {
private Map<Class<?>, InterA<?>> map = new HashMap<>();
public <T> void put(Class<T> key, InterA<T> value) {
map.put(key, value);
}
//we suppress unchecked cast warning because for Class<T> key we can put only InterA<T> value
#SuppressWarnings("unchecked")
public <T> InterA<T> get(Class<T> key) {
return (InterA<T>) map.get(key);
}
}
then you can use it like this
ClassMap map = new ClassMap();
map.put(Machine.class, new MachineOperator());
InterA<Machine> operator = map.get(Machine.class);
You should replace T in Map declaration with some common ancestor for Car and Machine.
If there is no common class, just use Object or ?.
That will eliminate compilation errors, but will increase chances of runtime errors. You will have be sure that the instance of the parameter is what is expected.
The Lower Bounded Wildcards is what you are looking for.
Your map declaration should looks like that:
Map<Class<?>, ? super InterA<?>> map = new HashMap<>();
I have the following interface (some methods ommited for simplicity):
public interface Container{
public void put(String s, Object o);
public Object get(String s);
}
and its implementation:
public class ContainerImpl implements Container{
private Map<Stirng, Object> m;
public ContainerImpl(){
m = new HashMap<>();
}
//This constructor is used in the case if a client is not satisfied
//by the HashMap, e.g. in case of Enum it's better to use EnumMap
protected ContainerImpl(Map<String, Object> m){
this.m = m;
}
public void put(String s, Object o){
m.put(s, o);
}
public Object get(String s){
m.get(s);
}
}
My question is about if providing such a protected constructor contraries to incapsulation. In fact we give to clients some knowledge that internally we use Map. If the dataStructure changed, we'll have to perform a conversion from the map passed as a parameter which may probably cause some bugs, I think.
if providing such a protected constructor contraries to incapsulation.
You are right, it does contradicts incapsulation behavior of ContainerImpl.
IMHO this is a design decision; whether class is designed to enforce incapsulation or to expose to client's/caller's for supporting varities of constructs.
For example:
A: ContainerImpl with only default-constructor implies that internal storage of Container is completely governed by it's concrete-implementation and caller cannot choose different storage.
B:
And ContainerImpl with
protected ContainerImpl(Map<String, Object> m)
implies that caller can choose the nature of Map based storage i.e. TreeMap, HashMap, LinkedHashMap or a custom implementation.
Decision on choosing one of the above approached would be based on client's need and nature.
You have responsibilities of creation, use and encapsulation of underlying Map in a single class.
If you want to follow SRP, try leaving the only public constructor which accepts Map as argument, use factories or descendants to encapsulate data:
/**
* #param m - storage model. Should not be modified after call.
*/
public ContainerImpl(Map<String, Object> m){
this.m = m;
}
/** A new instance with default storage model */
public static ContainerImpl createDefault() {
// Storage reference is isolated
return new ContainerImpl(new HashMap<>());
}
Alternatively, delete all constructors and provide:
protected abstract Map<String, Object> getStorage();
I would suggest to use something like this:
protected ContainerImpl(Map<String, Object> m){
this(); //default constructor, instantiates internal map
this.m.putAll(m); // copy all values
}
This way you will not affect encapsulation, but you will provide some convenience. As an alternative you could provide a factory method like this:
protected ContainerImpl create(Map<String, Object> m){
ContainerImpl impl = new ContainerImpl(); //default constructor, instantiates internal map
impl.m.putAll(m); // copy all values
return impl;
}
In java, how should I create a Map<String,String> that has unmodifiable keys, while keeping the values modifiable.
I'd like to hand this Map<String,String> through an interface for someone else to add/change the Map values, but not be able to change the Map keys.
The background on higher level problem is that I have list/set of variable names (with tree like structure) (represented as java String) that I'd like code on the other side of the java interface to be able to populate aliases (also Strings) for each of the variable names. I'd like to have multiple implementations of this interface so naming tree hierarchy can be aliases different ways to fit different situations. Having the interface implementation populate a Map<String,String> with bunch of keys already set-in-stone (and maybe containing defaults for the values) and allowing it to modify the values (but not the keys), seems like the best approach. I'm creating a mapping between names and alias, so Map<> makes sense.
Back to the lower level problem. I'd like my code to resemble:
public class MyClass
{
public interface IMyMapper
{
void build(Map<String,String> mapping);
}
IMyMapper mapper;
// How I'd like to use it
void work()
{
Map<String,String> map ;
// Magic something like Collections unmodifiableMap, but only for keys
// Maybe my question should be how this magic for UnmodifiableMap works, so I could reproduce it??
mapper.build(map);
// Because Maps<> are by reference, changed they made (to the values) would be reflected here
}
}
public class TheirClass implements MyClass.IMyMapper
{
#Override
public void build(Map<String,String> mapping)
{
// use mapping like Map<String,String> without extra/foreign classes
// but not be able to modify the Map keys
// only be able to change the Map values
// Should be able to use all of the awesome Map stuff, like foreach, values, compute
}
}
I know there is Collections unmodifiableMap(Map<> m) but that also makes the values unmodifiable. If my values were mutable objects, then I could modify them but I'd like to stick with Strings (avoiding creating Class with set/get for single String member, or creating Structure-like-class with public String member).
AKA, I'd like to avoid creating my own mutable class-values, and use Collections unmodifiableMap() to make the keys and value references unmodifiable:
// mutable reference to a String
public class ExtraWorkForForEveryone
{
public String value;
public void setValue(String value) { ... }
public String getValue() { ... }
}
// and then use:
void work()
{
Map<String,ExtraWorkForEveryone> map;
map = Collections.unmodifiableMap( ... );
// because Collections.unmodifiableMap() only stops them from changing the Map references,
// the interfacer could still change the ExtraWorkForEveryone internals.
// so they could not change keys refs or value refs, but they could change value data.
mapper.build(map);
// Because Maps<> are by reference, changed they made (to the values) would be reflected here
}
I could extend or implement my own Map, then (like how Collections unmodifiableMap()) override all methods that could change the keys throw UnsupportedOperationException. But with Java 8, there has been a large number of methods added using Lambda functions, which would be nice for Interface implementers to have access to, as long as they could not change the keys.
AKA, I'd like to avoid this lengthy and error-prone technique:
public final class FinalHashMap extends HashMap
{
#Override // anything that might be able to change the Map Keys
so_many_methods_and_edge_cases()
{ throws UnsupportedOperationException }
}
Is there existing interface that only allows changing the data of values of Maps<>?
What are my other options for creating something resembling a Map<String,String> that has unmodifiable keys, but modifiable values? I am interested in good coding practices, if possible.
Seems like you're looking for the Proxy Pattern.
Detailed answer:
The idea is to use what's called a proxy to interact with the map. The proxy will intercept all calls to the map; you should only be able to interact with the map through the proxy. It acts as an interface between the client and the map.
A proxy is a skeleton of what you are "wrapping". Since you are creating a proxy for a map, the proxy should implement the Map interface:
class ImmutableMap<K, V> implements Map<K, V> {
private Map<K, V> map;
public ImmutableMap(Map<K, V> map) {
this.map = new HashMap<>(map); // detach reference
}
//implement methods from Map
}
Most methods will simply telescope to map. Modify the methods you need to prevent removing keys or adding new keys to the map, such as put, putAll and remove:
final class ImmutableMap<K, V> implementsMap<K, V> {
private Map<K, V> map;
public ImmutableMap(Map<K, V> map) {
this.map = new HashMap<>(map);
}
#Override
public int size() {
return map.size();
}
#Override
public boolean isEmpty() {
return map.isEmpty();
}
#Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
#Override
public boolean containsValue(Object value) {
return map.containsValue(value);
}
#Override
public V get(Object key) {
return map.get(key);
}
#Override
public V put(K key, V value) {
if(!map.containsKey(key)) {
throw new IllegalArgumentException("Cannot add new keys!");
}
return map.put(key, value);
}
#Override
public V remove(Object key) {
throw new UnsupportedOperationException("You cannot remove entries from this map!");
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
for(K key : map.keySet()) {
if(!this.map.containsKey(key)) {
throw new IllegalArgumentException("Cannot add new keys to this map!");
}
}
this.map.putAll(map);
}
#Override
public void clear() {
throw new UnsupportedOperationException("You cannot remove entries from this map!");
}
#Override
public Set<K> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
#Override
public Collection<V> values() {
return Collections.unmodifiableSet(map.values()); //prevebt changing values to null
}
#Override
public Set<Map.Entry<K, V>> entrySet() {
//to allow modification of values, create your own ("immutable") entry set and return that
return Collections.unmodifiableSet(map.entrySet());
}
}
Keynotes:
Collections.unmodifiableSet should be used when returning sets from the map. This ensures that if a person attempts to modify a set returned from the map, it'll throw an UnsupportedOperationException
Creating a new Map containing the values of the map passed into the constructor prevents the client from modifying the ImmutableMap using the map they passed into it.
you may want to limit the size of your map
In over riding your put method you may use
if (map.size() == maxEntries) {
throw some exception;
I'm still unsure on how to explain this properly but I will give it my best shot:
I have a set of objects that have values inside that I would like to use as the key for a map.
In order to get the object I'm looking for, I could simply iterate over the set comparing the inner values as I go; this seems inefficient considering one of these values inside the object could be used as a key.
Now the issue is this: were I to change the value inside the object, I also have to update the key to the map, is there not a data structure that would facilitate this task?
In essence; I would like an auto-updating map with keys changing as the value inside changes... I would rather not have to store the map inside the value so, surely there's a more efficient way of thinking than my closed mind?
I hope I've explained myself well enough to get my thinking across.
If I'm getting you right, consider this code:
public class Main{
public static void main(String[] args) {
Test test = new Test(5);
Map<Test, String> map = new HashMap<Test, String>();
map.put(test, "I'm here");
test.value = 10;
System.out.print(map.get(test));
}
}
class Test{
public int value;
Test(int value){
this.value = value;
}
}
As the object you pass into the map as a key is passed by reference, any changes on the object will be reflected in the key set as well. This way you can use entire object as a key, not just the field it contains. You will need to be careful with providing proper equals() and hashCode() methods, though.
I would like an auto-updating map with keys changing as the value inside changes..
Well, you interesting to change parent a.e. storage (Map) on any child change (Value). I don't know if its good design.
If you change the key to Value, map doesn't change
So I would write custom Map like:
public class MyMap extends LinkedHashMap<String, Value> {/**/}
and there I would override put method and implement onValueChange() method by using Interface.
Each child will store instance of Map through interface.
So here we go ....:
RegisterItf
public interface RegisterItf {
public void onValueChange(String newKey, String oldKey, Value newValue);
}
MyMap
public class MyMap extends LinkedHashMap<String, Value> implements RegisterItf{
private static final long serialVersionUID = 1L;
#Override
public Value put(String key, Value value) {
value.setReg(this); // send map instance to each child who registered
return super.put(key, value);
}
#Override
public void onValueChange(String newKey, String oldKey, Value newValue) {
//here you remove old key and add new one
}
}
Value
public class Value {
private String mSomeData;
private String mKey;
private RegisterItf reg;
public void setmKey(String key) {
if(reg != null){
reg.onValueChange(key, mKey, this);
}
this.mKey = key;
}
public void setReg(RegisterItf reg) {
this.reg = reg;
}
}
main
MyMap map = new MyMap();
Value val = new Value();
val.setmKey("aa");
val.setmSomeData("blabla");
map.put(val.getmKey(), val);
...