Related
I have a problem with my custom iterator, so I'm asking for your help. I have class MyIterator, which is an iterator with transformation. This class has methods:
next() - returns next element
hasNext() - check if next element exists
fromIterator - static method, which converts Iterator to MyIterator
map - method which takes functional interface and returns MyIterator with transformation rule corresponding to this interface
forEach - method which takes functional interface and iterates over all remaining objects according to the interface. My realisation is
import java.util.Iterator;
import java.util.function.Consumer;
import java.util.function.Function;
public class MyIterator<K, V> {
private final Iterator<K> iterator;
private final Function<K, V> function;
#SuppressWarnings("unchecked")
public static <K, V> MyIterator<K, V> fromIterator(Iterator<K> iterator) {
return new MyIterator<>(iterator, k -> (V) k);
}
private MyIterator(Iterator<K> iterator, Function<K, V> function) {
this.iterator = iterator;
this.function = function;
}
public V next() {
return this.function.apply(iterator.next());
}
public boolean hasNext() {
return this.iterator.hasNext();
}
public MyIterator<K, V> map(Function<K, V> function) {
return new MyIterator<K, V>(this.iterator, this.function);
}
public void forEach(Consumer<V> action) {
while (hasNext()) {
action.accept(this.next());
}
}
}
So, I did this task, but I can't understand, how to change the method map into chaining method (pipeline). I mean the following:
MyIterator<String, Integer> myIterator3 = MyIterator.fromIterator(stringsArray.iterator()).map(s -> s.length()).map(i -> i.toString()).map(s -> s.length());
For example, I have String "England". After first map I want to get 7 ("England" consists of 7 characters), than "7", than 1 (because String "7" consists of 1 character). My assumption is that I should use methods andThen/compose in my method map, by I can't understand, how.
As Iterator accepts one parameter, here is how I modified your code.
public class ChainedIterator<T> {
private Function<T, ?> action;
private ChainedIterator<T> chain;
private final Iterator<?> iterator;
private <R> ChainedIterator(Iterator<?> iterator, Function<T, R> action, ChainedIterator<T> prev) {
this.action = action;
this.chain = prev;
this.iterator = iterator;
}
public static <T> ChainedIterator<T> fromIterator(Iterator<T> iterator) {
return new ChainedIterator<>(iterator, Function.identity(), null);
}
public T next() {
return (T) this.action.apply((T) (Objects.nonNull(this.chain) ? this.chain.next() : this.iterator.next()));
}
public boolean hasNext() {
return this.iterator.hasNext();
}
public <R> ChainedIterator<R> map(Function<T, R> action) {
return new ChainedIterator(this.iterator, action, this);
}
public void forEach(Consumer<T> action) {
while (hasNext()) {
action.accept(this.next());
}
}
}
Usage Example
Iterator<String> stringIterator = Arrays.asList("England", "India").iterator();
ChainedIterator<Integer> iterator = ChainedIterator.fromIterator(stringIterator)
.map(s -> s.length())
.map(i -> String.valueOf(i))
.map(s -> s.length());
I hope this helps :)
Update your custom Iterator and allow fromIterator() takes function rather you define it
public class MyIterator<K, V> {
private Iterator<K> iterator;
private List<Function<K, ?>> functions;
public static <K, V> MyIterator<K, V> fromIterator(Iterator<K> iterator) {
return new MyIterator<>(iterator);
}
private MyIterator(Iterator<K> iterator) {
this.iterator = iterator;
functions = new ArrayList<>();
}
private MyIterator(Iterator<K> iterator, Function<K, ?> function) {
this.iterator = iterator;
functions = new ArrayList<>();
functions.add(function);
}
private MyIterator(Iterator<K> iterator, List<Function<K, ?>> functions) {
this.iterator = iterator;
this.functions = functions;
}
public Object next() {
K key = iterator.next();
Object val = null;
for (int i = 0; i < functions.size(); i++) {
val = functions.get(i).apply(key);
key = (K) val;
}
return val;
}
public boolean hasNext() {
return iterator.hasNext();
}
public <R, RR> MyIterator<R, RR> map(Function<K, R> function) {
List<Function<K, ?>> functions2 = this.functions;
functions2.add(function);
return new MyIterator(iterator, functions2);
}
public void forEach(Consumer<Object> action) {
while (hasNext()) {
action.accept(next());
}
}
}
, main
public static void main(String[] args) throws Exception {
Iterator<String> sIterator = Arrays.asList("aaa", "bbbb", "cccc", "ddddd").iterator();
MyIterator.<String, Object>fromIterator(sIterator).map(s -> s.length()).map(i -> i + "")
.map(str -> str.length()).forEach(System.out::println);
}
, output
1
1
1
1
I was trying to implement a LRU cache using LinkedHashMap.
In the documentation of LinkedHashMap (http://docs.oracle.com/javase/7/docs/api/java/util/LinkedHashMap.html), it says:
Note that insertion order is not affected if a key is re-inserted into the map.
But when I do the following puts
public class LRUCache<K, V> extends LinkedHashMap<K, V> {
private int size;
public static void main(String[] args) {
LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
cache.put(1, 1);
cache.put(2, 2);
cache.put(1, 1);
cache.put(3, 3);
System.out.println(cache);
}
private LRUCache(int size) {
super(size, 0.75f, true);
this.size = size;
}
#Override
protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
return size() > size;
}
public static <K, V> LRUCache<K, V> newInstance(int size) {
return new LRUCache<K, V>(size);
}
}
The output is
{1=1, 3=3}
Which indicates that the re-inserted did affected the order.
Does anybody know any explanation?
As pointed out by Jeffrey, you are using accessOrder. When you created the LinkedHashMap, the third parameter specify how the order is changed.
"true for access-order, false for insertion-order"
For more detailed implementation of LRU, you can look at this
http://www.programcreek.com/2013/03/leetcode-lru-cache-java/
But you aren't using insertion order, you're using access order.
order of iteration is the order in which its entries were last
accessed, from least-recently accessed to most-recently (access-order)
...
Invoking the put or get method results in an access to the
corresponding entry
So this is the state of your cache as you modify it:
LRUCache<Integer, Integer> cache = LRUCache.newInstance(2);
cache.put(1, 1); // { 1=1 }
cache.put(2, 2); // { 1=1, 2=2 }
cache.put(1, 1); // { 2=2, 1=1 }
cache.put(3, 3); // { 1=1, 3=3 }
Here is my implementation by using LinkedHashMap in AccessOrder. It will move the latest accessed element to the front which only incurs O(1) overhead because the underlying elements are organized in a doubly-linked list while also are indexed by hash function. So the get/put/top_newest_one operations all cost O(1).
class LRUCache extends LinkedHashMap<Integer, Integer>{
private int maxSize;
public LRUCache(int capacity) {
super(capacity, 0.75f, true);
this.maxSize = capacity;
}
//return -1 if miss
public int get(int key) {
Integer v = super.get(key);
return v == null ? -1 : v;
}
public void put(int key, int value) {
super.put(key, value);
}
#Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return this.size() > maxSize; //must override it if used in a fixed cache
}
}
Technically LinkedHashMap has the following constructor. Which help us to make the access-order True/False. If it is false then it keep the insertion-order.
LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)
(#Constructs an empty LinkedHashMap instance with the specified initial capacity, load factor and ordering mode)
Following is the simple implementation of LRU Cache ---
class LRUCache {
private LinkedHashMap<Integer, Integer> linkHashMap;
public LRUCache(int capacity) {
linkHashMap = new LinkedHashMap<Integer, Integer>(capacity, 0.75F, true) {
#Override
protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
return size() > capacity;
}
};
}
public void put(int key, int value) {
linkHashMap.put(key, value);
}
public int get(int key) {
return linkHashMap.getOrDefault(key, -1);
}
}
I used the following code and its works!!!!
I have taken window size to be 4, but any value can be taken.
for Insertion order:
1: Check if the key is present.
2: If yes, then remove it (by using lhm.remove(key))
3: Add the new Key Value pair.
for Access Order:
No need of removing keys only put and get statements do everything automatically.
This code is for ACCESS ORDER:
import java.util.LinkedHashMap;
public class LRUCacheDemo {
public static void main(String args[]){
LinkedHashMap<String,String> lhm = new LinkedHashMap<String,String>(4,0.75f,true) {
#Override
protected boolean removeEldestEntry(Map.Entry<String,String> eldest) {
return size() > 4;
}
};
lhm.put("test", "test");
lhm.put("test1", "test1");
lhm.put("1", "abc");
lhm.put("test2", "test2");
lhm.put("1", "abc");
lhm.put("test3", "test3");
lhm.put("test4", "test4");
lhm.put("test3", "test3");
lhm.put("1", "abc");
lhm.put("test1", "test1");
System.out.println(lhm);
}
}
I also implement LRU cache with little change in code. I have tested and it works perfectly as concept of LRU cache.
package com.first.misc;
import java.util.LinkedHashMap;
import java.util.Map;
public class LRUCachDemo {
public static void main(String aa[]){
LRUCache<String, String> lruCache = new LRUCache<>(3);
lruCache.cacheable("test", "test");
lruCache.cacheable("test1", "test1");
lruCache.cacheable("test2", "test2");
lruCache.cacheable("test3", "test3");
lruCache.cacheable("test4", "test4");
lruCache.cacheable("test", "test");
System.out.println(lruCache.toString());
}
}
class LRUCache<K, T>{
private Map<K,T> cache;
private int windowSize;
public LRUCache( final int windowSize) {
this.windowSize = windowSize;
this.cache = new LinkedHashMap<K, T>(){
#Override
protected boolean removeEldestEntry(Map.Entry<K, T> eldest) {
return size() > windowSize;
}
};
}
// put data in cache
public void cacheable(K key, T data){
// check key is exist of not if exist than remove and again add to make it recently used
// remove element if window size is exhaust
if(cache.containsKey(key)){
cache.remove(key);
}
cache.put(key,data);
}
// evict functioanlity
#Override
public String toString() {
return "LRUCache{" +
"cache=" + cache.toString() +
", windowSize=" + windowSize +
'}';
}
}
I've created a hashmap with .class objects for keys.
Hashmap<Class<? extends MyObject>, Object> mapping = new Hashmap<Class<? extends MyObject>, Object>();
This is all well and fine, but I'm getting strange behaviour that I can only attribute to strangeness with the hash function. Randomly during runtime, iterating through the hashmap will not hit every value; it will miss one or two. I think this may be due to the .class object not being final, and therefore it changes causing it to map to a different hash value. With a different hash value, the hashmap wouldn't be able to correctly correlate the key with the value, thus making it appear to have lost the value.
Am I correct that this is what is going on? How can I work around this? Is there a better way to accomplish this form of data structure?
Edit: I really thought I was onto something with the hash function thing, but I'll post my real code to try and figure this out. It may be a problem with my implementation of a multimap. I've been using it for quite some time and haven't noticed any issues until recently.
/**
* My own implementation of a map that maps to a List. If the key is not present, then
* the map adds a List with a single entry. Every subsequent addition to the key
* is appended to the List.
* #author
*
* #param <T> Key
* #param <K> Value
*/
public class MultiMap<T, K> implements Map<T, List<K>>, Serializable, Iterable<K> {
/**
*
*/
private static final long serialVersionUID = 5789101682525659411L;
protected HashMap<T, List<K>> set = new HashMap<T, List<K>>();
#Override
public void clear() {
set = new HashMap<T, List<K>>();
}
#Override
public boolean containsKey(Object arg0) {
return set.containsKey(arg0);
}
#Override
public boolean containsValue(Object arg0) {
boolean output = false;
for(Iterator<List<K>> iter = set.values().iterator();iter.hasNext();) {
List<K> searchColl = iter.next();
for(Iterator<K> iter2 = searchColl.iterator(); iter2.hasNext();) {
K value = iter2.next();
if(value == arg0) {
output = true;
break;
}
}
}
return output;
}
#Override
public Set<Entry<T, List<K>>> entrySet() {
Set<Entry<T, List<K>>> output = new HashSet<Entry<T,List<K>>>();
for(Iterator<T> iter1 = set.keySet().iterator(); iter1.hasNext();) {
T key = iter1.next();
for(Iterator<K> iter2 = set.get(key).iterator(); iter2.hasNext();) {
K value = iter2.next();
List<K> input = new ArrayList<K>();
input.add(value);
output.add(new AbstractMap.SimpleEntry<T,List<K>>(key, input));
}
}
return output;
}
#Override
public boolean isEmpty() {
return set.isEmpty();
}
#Override
public Set<T> keySet() {
return set.keySet();
}
#Override
public int size() {
return set.size();
}
#Override
public Collection<List<K>> values() {
Collection<List<K>> values = new ArrayList<List<K>>();
for(Iterator<T> iter1 = set.keySet().iterator(); iter1.hasNext();) {
T key = iter1.next();
values.add(set.get(key));
}
return values;
}
#Override
public List<K> get(Object key) {
return set.get(key);
}
#Override
public List<K> put(T key, List<K> value) {
return set.put(key, value);
}
public void putValue(T key, K value) {
if(set.containsKey(key)) {
set.get(key).add(value);
}
else {
List<K> setval = new ArrayList<K>();
setval.add(value);
set.put(key, setval);
}
}
#Override
public List<K> remove(Object key) {
return set.remove(key);
}
public K removeValue(Object value) {
K valueRemoved = null;
for(T key:this.keySet()) {
for(K val:this.get(key)) {
if(val.equals(value)) {
List<K> temp = this.get(key);
temp.remove(value);
valueRemoved = val;
this.put(key, temp);
}
}
}
return valueRemoved;
}
#Override
public void putAll(Map<? extends T, ? extends List<K>> m) {
for(Iterator<? extends T> iter = m.keySet().iterator(); iter.hasNext();) {
T key = iter.next();
set.put(key, m.get(key));
}
}
#Override
public Iterator<K> iterator() {
return new MultiMapIterator<K>(this);
}
}
Perhaps there is an issue with my iterator? I'll post that code as well.
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
public class MultiMapIterator<T> implements Iterator<T> {
private MultiMap <?, T> map;
private Iterator<List<T>> HashIter;
private Iterator<T> govIter;
private T value;
public MultiMapIterator(MultiMap<?, T> map) {
this.map = map;
HashIter = map.values().iterator();
if(HashIter.hasNext()) {
govIter = HashIter.next().iterator();
}
if(govIter.hasNext()) {
value = govIter.next();
}
}
#Override
public boolean hasNext() {
if (govIter.hasNext()) {
return true;
}
else if(HashIter.hasNext()) {
govIter = HashIter.next().iterator();
return this.hasNext();
}
else {
return false;
}
}
#Override
public T next() {
if(!this.hasNext()) {
throw new NoSuchElementException();
}
else {
value = govIter.next();
return value;
}
}
#Override
public void remove() {
map.remove(value);
}
}
Sorry for the long tracts of code. Thank you for spending time helping me with this.
You pull the a value out of govIter in the constructor, but never return it.
Your iterator remove method is completely wrong. You are iterating values, but calling the map.remove which removes by key. you simply want to call govIter.remove() (unless you need to avoid empty lists, in which case it's more complicated).
Your hasNext() method could also have problems depending on whether or not you allow empty Lists values in your multimap.
Is there an idiomatic way to take a Set<K> and a Function<K,V>, and get a Map<K,V> live view? (i.e. the Map is backed by the Set and Function combo, and if e.g. an element is added to the Set, then the corresponding entry also exists in the Map).
(see e.g. Collections2.filter for more discussion on live views)
What if a live view is not needed? Is there something better than this:
public static <K,V> Map<K,V> newMapFrom(Set<K> keys, Function<? super K,V> f) {
Map<K,V> map = Maps.newHashMap();
for (K k : keys) {
map.put(k, f.apply(k));
}
return map;
}
Creating a Map from a Set and a Function
Here are two classes that should each do the job. The first just shows a map view of the set, while the second can write values back to the set through a special interface.
Call Syntax:
Map<K,V> immutable = new SetBackedMap<K,V>(Set<K> keys, Function<K,V> func);
Map<K,V> mutable = new MutableSetBackedMap<K,V>(Set<K> keys, Function<K,V> func);
Where to put this code?
Side note: If guava were my library, I'd make them accessible through the Maps class:
Map<K,V> immutable = Maps.immutableComputingMap(Set<K> keys, Function<K,V> func);
Map<K,V> mutable = Maps.mutableComputingMap(Set<K> keys, Function<K,V> func);
Immutable version:
I have implemented this as a one-way view:
Changes to the set are reflected in
the map, but not vice-versa (and you can't change the map anyway, the put(key, value) method isn't implemented).
The entrySet() iterator uses the
set iterator internally, so it will
also inherit the internal iterator's
handling of
ConcurrentModificationException.
Both put(k,v) and
entrySet().iterator().remove() will
throw
UnsupportedOperationException.
Values are cached in a WeakHashMap,
with no special concurrency handling, i.e. there is no synchronization at
any level. This will do for most cases, but if your function is expensive, you might want to add some locking.
Code:
public class SetBackedMap<K, V> extends AbstractMap<K, V>{
private class MapEntry implements Entry<K, V>{
private final K key;
public MapEntry(final K key){
this.key = key;
}
#Override
public K getKey(){
return this.key;
}
#Override
public V getValue(){
V value = SetBackedMap.this.cache.get(this.key);
if(value == null){
value = SetBackedMap.this.funk.apply(this.key);
SetBackedMap.this.cache.put(this.key, value);
}
return value;
}
#Override
public V setValue(final V value){
throw new UnsupportedOperationException();
}
}
private class EntrySet extends AbstractSet<Entry<K, V>>{
public class EntryIterator implements Iterator<Entry<K, V>>{
private final Iterator<K> inner;
public EntryIterator(){
this.inner = EntrySet.this.keys.iterator();
}
#Override
public boolean hasNext(){
return this.inner.hasNext();
}
#Override
public Map.Entry<K, V> next(){
final K key = this.inner.next();
return new MapEntry(key);
}
#Override
public void remove(){
throw new UnsupportedOperationException();
}
}
private final Set<K> keys;
public EntrySet(final Set<K> keys){
this.keys = keys;
}
#Override
public Iterator<Map.Entry<K, V>> iterator(){
return new EntryIterator();
}
#Override
public int size(){
return this.keys.size();
}
}
private final WeakHashMap<K, V> cache;
private final Set<Entry<K, V>> entries;
private final Function<? super K, ? extends V> funk;
public SetBackedMap(
final Set<K> keys, Function<? super K, ? extends V> funk){
this.funk = funk;
this.cache = new WeakHashMap<K, V>();
this.entries = new EntrySet(keys);
}
#Override
public Set<Map.Entry<K, V>> entrySet(){
return this.entries;
}
}
Test:
final Map<Integer, String> map =
new SetBackedMap<Integer, String>(
new TreeSet<Integer>(Arrays.asList(
1, 2, 4, 8, 16, 32, 64, 128, 256)),
new Function<Integer, String>(){
#Override
public String apply(final Integer from){
return Integer.toBinaryString(from.intValue());
}
});
for(final Map.Entry<Integer, String> entry : map.entrySet()){
System.out.println(
"Key: " + entry.getKey()
+ ", value: " + entry.getValue());
}
Output:
Key: 1, value: 1
Key: 2, value: 10
Key: 4, value: 100
Key: 8, value: 1000
Key: 16, value: 10000
Key: 32, value: 100000
Key: 64, value: 1000000
Key: 128, value: 10000000
Key: 256, value: 100000000
Mutable Version:
While I think it's a good idea to make this one-way, here's a version for Emil that provides a two-way view (it's a variation of Emil's variation of my solution :-)). It requires an extended map interface that I'll call ComputingMap to make clear that this is a map where it doesn't make sense to call put(key, value).
Map interface:
public interface ComputingMap<K, V> extends Map<K, V>{
boolean removeKey(final K key);
boolean addKey(final K key);
}
Map implementation:
public class MutableSetBackedMap<K, V> extends AbstractMap<K, V> implements
ComputingMap<K, V>{
public class MapEntry implements Entry<K, V>{
private final K key;
public MapEntry(final K key){
this.key = key;
}
#Override
public K getKey(){
return this.key;
}
#Override
public V getValue(){
V value = MutableSetBackedMap.this.cache.get(this.key);
if(value == null){
value = MutableSetBackedMap.this.funk.apply(this.key);
MutableSetBackedMap.this.cache.put(this.key, value);
}
return value;
}
#Override
public V setValue(final V value){
throw new UnsupportedOperationException();
}
}
public class EntrySet extends AbstractSet<Entry<K, V>>{
public class EntryIterator implements Iterator<Entry<K, V>>{
private final Iterator<K> inner;
public EntryIterator(){
this.inner = MutableSetBackedMap.this.keys.iterator();
}
#Override
public boolean hasNext(){
return this.inner.hasNext();
}
#Override
public Map.Entry<K, V> next(){
final K key = this.inner.next();
return new MapEntry(key);
}
#Override
public void remove(){
throw new UnsupportedOperationException();
}
}
public EntrySet(){
}
#Override
public Iterator<Map.Entry<K, V>> iterator(){
return new EntryIterator();
}
#Override
public int size(){
return MutableSetBackedMap.this.keys.size();
}
}
private final WeakHashMap<K, V> cache;
private final Set<Entry<K, V>> entries;
private final Function<? super K, ? extends V> funk;
private final Set<K> keys;
public MutableSetBackedMap(final Set<K> keys,
final Function<? super K, ? extends V> funk){
this.keys = keys;
this.funk = funk;
this.cache = new WeakHashMap<K, V>();
this.entries = new EntrySet();
}
#Override
public boolean addKey(final K key){
return this.keys.add(key);
}
#Override
public boolean removeKey(final K key){
return this.keys.remove(key);
}
#Override
public Set<Map.Entry<K, V>> entrySet(){
return this.entries;
}
}
Test:
public static void main(final String[] args){
final ComputingMap<Integer, String> map =
new MutableSetBackedMap<Integer, String>(
new TreeSet<Integer>(Arrays.asList(
1, 2, 4, 8, 16, 32, 64, 128, 256)),
new Function<Integer, String>(){
#Override
public String apply(final Integer from){
return Integer.toBinaryString(from.intValue());
}
});
System.out.println(map);
map.addKey(3);
map.addKey(217);
map.removeKey(8);
System.out.println(map);
}
Output:
{1=1, 2=10, 4=100, 8=1000, 16=10000, 32=100000, 64=1000000, 128=10000000, 256=100000000}
{1=1, 2=10, 3=11, 4=100, 16=10000, 32=100000, 64=1000000, 128=10000000, 217=11011001, 256=100000000}
Caution. Sean Patrick Floyd's answer, although very useful, has a flaw. A simple one, but took me a while to debug so don't fall in the same trap: the MapEntry class requires equals and hashcode implementations. Here are mine (simple copy from the javadoc).
#Override
public boolean equals(Object obj) {
if (!(obj instanceof Entry)) {
return false;
}
Entry<?, ?> e2 = (Entry<?, ?>) obj;
return (getKey() == null ? e2.getKey() == null : getKey().equals(e2.getKey()))
&& (getValue() == null ? e2.getValue() == null : getValue().equals(e2.getValue()));
}
#Override
public int hashCode() {
return (getKey() == null ? 0 : getKey().hashCode()) ^
(getValue() == null ? 0 : getValue().hashCode());
}
This reply would be better as a commentary to the relevant answer, but AFAIU I don't have the right to post a comment (or did't find how to!).
Guava 14 now has Maps.asMap for a view of the Set and Maps.toMap for an immutable copy.
You can see much of the discussion of the issues involved here:
https://github.com/google/guava/issues/56
For the non live view the code exists in lambdaJ with Lambda.map(Set, Converter).
Set<K> setKs = new Set<K>();
Converter<K, V> converterKv = new Converter<K,V>{
#Override
public V convert(K from){
return null; //Not useful here but you can do whatever you want
}
}
Map<K, V> mapKvs = Lambda.map(setKs, converterKv);
I tried my own implementation : http://ideone.com/Kkpcn
As said in the comments, I have to extends another class so I just implemented Map, that's why there is so much code.
There is a totally useless (or not ?) feature that allows you to change the converter on the fly.
what about Maps.uniqueIndex()
I don't know if this is what you mean by live view.Any way here is my try.
public class GuavaTst {
public static void main(String[] args) {
final Function<String, String> functionToLower = new Function<String, String>() {
public String apply (String input) {
return input.toLowerCase();
}
};
final Set<String> set=new HashSet<String>();
set.add("Hello");
set.add("BYE");
set.add("gOOd");
Map<String, String> testMap = newLiveMap(set,functionToLower);
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
set.add("WoRld");
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
testMap.put("OMG","");
System.out.println("Map :- "+testMap);
System.out.println("Set :- "+set);
}
static <K,V> Map<K,V> newLiveMap(final Set<K> backEnd,final Function<K,V> fun)
{
return new HashMap<K,V>(){
#Override
public void clear() {
backEnd.clear();
}
#Override
public boolean containsKey(Object key) {
return backEnd.contains(key);
}
#Override
public boolean isEmpty() {
return backEnd.isEmpty();
}
#Override
public V put(K key, V value) {
backEnd.add(key);
return null;
}
#Override
public boolean containsValue(Object value) {
for(K s:backEnd)
if(fun.apply(s).equals(value))
return true;
return false;
}
#Override
public V remove(Object key) {
backEnd.remove(key);
return null;
}
#Override
public int size() {
return backEnd.size();
}
#Override
public V get(Object key) {
return fun.apply((K)key);
}
#Override
public String toString() {
StringBuilder b=new StringBuilder();
Iterator<K> itr=backEnd.iterator();
b.append("{");
if(itr.hasNext())
{
K key=itr.next();
b.append(key);
b.append(":");
b.append(this.get(key));
while(itr.hasNext())
{
key=itr.next();
b.append(", ");
b.append(key);
b.append(":");
b.append(this.get(key));
}
}
b.append("}");
return b.toString();
}
};
}
}
The implementation is not complete and the overridden functions are not tested but I hope it convey's the idea.
UPDATE:
I made some small change's to seanizer's answer so that the changes made in map will reflect in the set also.
public class SetBackedMap<K, V> extends AbstractMap<K, V> implements SetFunctionMap<K, V>{
public class MapEntry implements Entry<K, V>{
private final K key;
public MapEntry(final K key){
this.key = key;
}
#Override
public K getKey(){
return this.key;
}
#Override
public V getValue(){
V value = SetBackedMap.this.cache.get(this.key);
if(value == null){
value = SetBackedMap.this.funk.apply(this.key);
SetBackedMap.this.cache.put(this.key, value);
}
return value;
}
#Override
public V setValue(final V value){
throw new UnsupportedOperationException();
}
}
public class EntrySet extends AbstractSet<Entry<K, V>>{
public class EntryIterator implements Iterator<Entry<K, V>>{
private final Iterator<K> inner;
public EntryIterator(){
this.inner = EntrySet.this.keys.iterator();
}
#Override
public boolean hasNext(){
return this.inner.hasNext();
}
#Override
public Map.Entry<K, V> next(){
final K key = this.inner.next();
return new MapEntry(key);
}
#Override
public void remove(){
throw new UnsupportedOperationException();
}
}
private final Set<K> keys;
public EntrySet(final Set<K> keys){
this.keys = keys;
}
#Override
public boolean add(Entry<K, V> e) {
return keys.add(e.getKey());
}
#Override
public Iterator<Map.Entry<K, V>> iterator(){
return new EntryIterator();
}
#Override
public int size(){
return this.keys.size();
}
#Override
public boolean remove(Object o) {
return keys.remove(o);
}
}
private final WeakHashMap<K, V> cache;
private final Set<Entry<K, V>> entries;
private final Function<K, V> funk;
public SetBackedMap(final Set<K> keys, final Function<K, V> funk){
this.funk = funk;
this.cache = new WeakHashMap<K, V>();
this.entries = new EntrySet(keys);
}
#Override
public Set<Map.Entry<K, V>> entrySet(){
return this.entries;
}
public boolean putKey(K key){
return entries.add(new MapEntry(key));
}
#Override
public boolean removeKey(K key) {
cache.remove(key);
return entries.remove(key);
}
}
Interface SetFunctionMap:
public interface SetFunctionMap<K,V> extends Map<K, V>{
public boolean putKey(K key);
public boolean removeKey(K key);
}
Test Code:
public class SetBackedMapTst {
public static void main(String[] args) {
Set<Integer> set=new TreeSet<Integer>(Arrays.asList(
1, 2, 4, 8, 16));
final SetFunctionMap<Integer, String> map =
new SetBackedMap<Integer, String>(set,
new Function<Integer, String>(){
#Override
public String apply(final Integer from){
return Integer.toBinaryString(from.intValue());
}
});
set.add(222);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
map.putKey(112);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
map.removeKey(112);
System.out.println("Map: "+map);
System.out.println("Set: "+set);
}
}
Output:
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 222=11011110}//change to set reflected in map
Set: [1, 2, 4, 8, 16, 222]
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 112=1110000, 222=11011110}
Set: [1, 2, 4, 8, 16, 112, 222]//change to map reflected in set
Map: {1=1, 2=10, 4=100, 8=1000, 16=10000, 222=11011110}
Set: [1, 2, 4, 8, 16, 222]//change to map reflected in set
I need to do a search in a map of maps and return the keys this element belong.
I think this implementation is very slow, can you help me to optimize it?.
I need to use TreeSet and I can't use contains because they use compareTo, and equals/compareTo pair are implemented in an incompatible way and I can't change that.
(sorry my bad english)
Map<Key, Map<SubKey, Set<Element>>> m = new TreeSet();
public String getKeys(Element element) {
for(Entry<Key, Map<SubKey, Set<Element>>> e : m.entrySet()) {
mapSubKey = e.getValue();
for(Entry<SubKey, Set<Element>> e2 : mapSubKey.entrySet()) {
setElements = e2.getValue();
for(Element elem : setElements)
if(elem.equals(element)) return "Key: " + e.getKey() + " SubKey: " + e2.getKey();
}
}
}
The problem here is that the keys and values are backward.
Maps allow one to efficiently find a value (which would be Key and SubKey) associated with a key (Element, in this example).
Going backwards is slow.
There are bi-directional map implementations, like Google Collections BiMap, that support faster access in both directions—but that would mean replacing TreeMap. Otherwise, maintain two maps, one for each direction.
if you can't use contains, and you're stuck using a Map of Maps, then your only real option is to iterate, as you are doing.
alternatively, you could keep a reverse map of Element to Key/SubKey in a separate map, which would make reverse lookups faster.
also, if you're not sure that a given Element can exist in only one place, you might want to store and retrieve a List<Element> instead of just an Element
Using TreeMap and TreeSet work properly when compareTo and equals are implemented in such a way that they are compatible with each other. Furthermore, when searching in a Map, only the search for the key will be efficient (for a TreeMap O(log n)). When searching for a value in a Map, the complexity will become linear.
There is a way to optimize the search in the inner TreeSet though, when implementing your own Comparator for the Element type. This way you can implement your own compareTo method without changing the Element object itself.
Here is a bidirectional TreeMap (or Bijection over TreeMap).
It associates two overloaded TreeMaps Which are tied together.
Each one "inverse" constant field points to the other TreeMap. Any change on one TreeMap is automatically reflected on its inverse.
As a result, each value is unique.
public class BiTreeMap<K, V> extends TreeMap<K, V> {
public final BiTreeMap<V, K> inverse;
private BiTreeMap(BiTreeMap inverse) {
this.inverse = inverse;
}
public BiTreeMap() {
inverse = new BiTreeMap<V, K>(this);
}
public BiTreeMap(Map<? extends K, ? extends V> m) {
inverse = new BiTreeMap<V, K>(this);
putAll(m);
}
public BiTreeMap(Comparator<? super K> comparator) {
super(comparator);
inverse = new BiTreeMap<V, K>(this);
}
public BiTreeMap(Comparator<? super K> comparatorK, Comparator<? super V> comparatorV) {
super(comparatorK);
inverse = new BiTreeMap<V, K>(this, comparatorV);
}
private BiTreeMap(BiTreeMap<V, K> inverse, Comparator<? super K> comparatorK) {
super(comparatorK);
this.inverse = inverse;
}
#Override
public V put(K key, V value) {
if(key == null || value == null) {
throw new NullPointerException();
}
V oldValue = super.put(key, value);
if (oldValue != null && inverse._compareKeys(value, oldValue) != 0) {
inverse._remove(oldValue);
}
K inverseOldKey = inverse._put(value, key);
if (inverseOldKey != null && _compareKeys(key, inverseOldKey) != 0) {
super.remove(inverseOldKey);
}
return oldValue;
}
private int _compareKeys(K k1, K k2) {
Comparator<? super K> c = comparator();
if (c == null) {
Comparable<? super K> ck1 = (Comparable<? super K>) k1;
return ck1.compareTo(k2);
} else {
return c.compare(k1, k2);
}
}
private V _put(K key, V value) {
return super.put(key, value);
}
#Override
public V remove(Object key) {
V value = super.remove(key);
inverse._remove(value);
return value;
}
private V _remove(Object key) {
return super.remove(key);
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
for (Map.Entry<? extends K, ? extends V> e : map.entrySet()) {
K key = e.getKey();
V value = e.getValue();
put(key, value);
}
}
#Override
public void clear() {
super.clear();
inverse._clear();
}
private void _clear() {
super.clear();
}
public boolean containsValue(Object value) {
return inverse.containsKey(value);
}
#Override
public Map.Entry<K, V> pollFirstEntry() {
Map.Entry<K, V> entry = super.pollFirstEntry();
inverse._remove(entry.getValue());
return entry;
}
#Override
public Map.Entry<K, V> pollLastEntry() {
Map.Entry<K, V> entry = super.pollLastEntry();
inverse._remove(entry.getValue());
return entry;
}
}