Is there softreference-based LinkedHashMap in Java? If no, has anyone got a snippet of code that I can probably reuse? I promise to reference it correctly.
Thanks.
WeakHashMap doesn't preserve the insertion order. It thus cannot be considered as a direct replacement for LinkedHashMap. Moreover, the map entry is only released when the key is no longer reachable. Which may not be what you are looking for.
If what you are looking for is a memory-friendly cache, here is a naive implementation you could use.
package be.citobi.oneshot;
import java.lang.ref.SoftReference;
import java.util.LinkedHashMap;
public class SoftLinkedCache<K, V>
{
private static final long serialVersionUID = -4585400640420886743L;
private final LinkedHashMap<K, SoftReference<V>> map;
public SoftLinkedCache(final int cacheSize)
{
if (cacheSize < 1)
throw new IllegalArgumentException("cache size must be greater than 0");
map = new LinkedHashMap<K, SoftReference<V>>()
{
private static final long serialVersionUID = 5857390063785416719L;
#Override
protected boolean removeEldestEntry(java.util.Map.Entry<K, SoftReference<V>> eldest)
{
return size() > cacheSize;
}
};
}
public synchronized V put(K key, V value)
{
SoftReference<V> previousValueReference = map.put(key, new SoftReference<V>(value));
return previousValueReference != null ? previousValueReference.get() : null;
}
public synchronized V get(K key)
{
SoftReference<V> valueReference = map.get(key);
return valueReference != null ? valueReference.get() : null;
}
}
The best idea I've seen for this is wrapping LinkedHashMap so that everything you put into it is a WeakReference.
UPDATE: Just browsed the source of WeakHashMap and the way it handles making everything a WeakReference while still playing nice with generics is solid. Here's the core class signature it uses:
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V>
I suggest browsing the source more in depth for other implementation ideas.
UPDATE 2: kdgregory raises a good point in his comment - all my suggestion does is make sure the references in the Map won't keep the referent from being garbage-collected. You still need to clean out the dead references manually.
have a look at this post. It shows how to implement a SoftHashMap...
Related
While going through the HashMap implementation and some tutorials, I thought of this question. But not getting how to solve this. So posting here if you guys can help me out. I want to keep a count of all the calls to add each key-value pair in a Map. It should count the calls to put, putAll and putIfAbsent all the methods too.
Primarily there are two options,
Use inheritance
Assuming the Map implementation you want to track allows extension, then create a subclass and override the required methods.
In the overridden methods, do your custom logic(count) and then invoke the super. method of the base class(actual method)
Major advantage of this approach, instanceof and generics compiler behavior will be favorable.
This approach has a major problem, that you need to extend every Map class that needs to be supported. It will result is explosion of class.
Use composition
Add a new class that implements Map (per top level related type)
Have a reference variable of the actual Map
Implement the methods from Map interface
In the implementation, do your tracking operation and invoke the map pointed by the reference variable
The major advantage of this method is, there be very few new class.
The disadvantage of this behavior is instanceof and generic types might not work as expected by user.
Suggestion
If you are planning to use this in some large scale application, then discuss more about the future possible use cases and also abstract the Map creation part to some factory method or even factory.
This will allow you to encapsulate the implementation details within the factories.
Or try some way of doing with method interceptors. Though this can increase latencies slightly, this will not require much change from clients and also avoids maintaining a parallel hierarchy of Map implementations.
If using interceptors, i don't know the level of detail you can control. Again this will also be complex.
Learning these techniques is good. Please don't use them in production code until it's the only possible option.
Note
Define the meaning of count precisely
Whether calls only from users of this class should be counted?
Or, even the calls arising within this class can be counted (like add calling get to find presence) ?
You should use Decorator pattern to add this functionality dynamically to any map.
Create a class implementing interface Map<K, V> with a final field Map<K, V> map and appropriate counter(s) for the stats, and counter field(s) with getter(s).
Override methods put, putAll, putIfAbsent to increment counter(s)
Implement remaining methods of Map interface by delegating calls to map field.
Of course, the statistics of putXxx operations would be valid only since a map is "decorated".
class PutStatsMapDecorator<K, V> implements Map<K, V> {
private final Map<K, V> map; // map being decorated
private int countPut;
private int countPutAll;
private int countPutIfAbsent;
public PutStatsMapDecorator(Map<K, V> map) {
this.map = map;
}
// stats getters
public int getCountPut() { return countPut; }
public int getCountPutAll() { return countPutAll; }
public int getCountPutIfAbsent() { return countPutIfAbsent; }
// override appropriate methods to update counters
#Override public V put(K key, V value) {
countPut++;
return map.put(key, value);
}
#Override public void putAll(Map<? extends K, ? extends V> m) {
countPutAll++;
map.putAll(m);
}
#Override public V putIfAbsent(K key, V value) {
countPutIfAbsent++;
return map.putIfAbsent(key, value);
}
// implement remaining methods of Map interface by delegating calls to `map` field
#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 remove(Object key) { return map.remove(key); }
#Override public void clear() { map.clear(); }
#Override public Set<K> keySet() { return map.keySet(); }
#Override public Collection<V> values() { return map.values(); }
#Override public Set<Entry<K, V>> entrySet() { return map.entrySet(); }
}
You can create a wrapper that implements the Map interface and takes the map where you want to log the calls as constructor parameter. Then you delegate all calls to that map and additionally do the logging:
public class LoggingMap<K,V> implements Map<K,V> {
private Map<K,V> wrapped;
public LoggingMap<K,V> (Map <K,V> wrapping) {
wrapped = wrapping;
}
#Override
public xy(...) {
System.out.println("calling xy"); // log the call
wrapped.xy(...); // delegate to the original map
}
// repeat for *all* methods of the map interface
}
Using proxy can help, for example,
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
public class MethodCounter {
public static HashMap<String, Integer> counts = new HashMap<String, Integer>();
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HashMap.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> {
if (counts.containsKey(method.getName()))
counts.put(method.getName(), counts.get(method.getName()) + 1);
else
counts.put(method.getName(), 1);
return proxy.invokeSuper(obj, args1);
});
Map<String, String> testMap = (HashMap<String, String>) enhancer.create();
testMap.put("1", "1");
testMap.put("2", "1");
testMap.put("3", "1");
testMap.put("4", "1");
testMap.get("1");
testMap.get("1");
testMap.get("1");
testMap.get("1");
testMap.get("1");
for (Entry<String, Integer> entry : counts.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}
It requires cglib dependency,
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
</dependency>
The output of above program is,
get: 5
put: 4
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.
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 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.
how can I prevent hashmap or treemap from replacing the previous key value if already present?Also,I want to throw an exception to notify the user.
Any such map would be violating the normal Map interface, to be honest. But if you're happy to do that, you could easily create your own Map implementation which delegates to another map, but only after checking for the presence of an existing element:
public final class NonReplacementMap<K, V> implements Map<K, V> {
private final Map<K, V> original;
public NonReplacementMap(Map<K, V> original) {
this.original = original;
}
#Override
public void put(K key, V value) {
if (original.containsKey(key)) {
// Or whatever unchecked exception you want
throw new IllegalStateException("Key already in map");
}
original.put(key, value);
}
// etc
}
Use myMap.putIfAbsent(key, val)
This has been introduced in the Map interface since 1.8
* #since 1.8
*/
default V putIfAbsent(K key, V value) {
Why not just wrap your map? Composition over inheritance.
void put(Object key, Object value){
if(map.containsKey(key)){
throw Exception("Custom exception");
}else{
map.put(key,value);
}
}
You can use Map#containsKey(object) method to check if key is there already before adding it to the Map
No,by default you cannot,because it seems specific requirement for you.
So create your own class which extends HashMap/any implementation and ovveride inserting methods .
If key matches with existing one return from there/throw some exception.