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
Related
I have a database keeping key-value objects. A key maybe composite with a separator, e.g. stackoverflow.questions.ask. I want to map such object to JSON. Thus, for stackoverflow.questions.ask key I want to get
{
'stackoverflow' : {
'questions' : {
'ask' : 'value'
}
}
}
Is there any simple way to do it automatically?
I don't think Gson is a right tool here, but I really like this question.
This is a general algorithm and you can implement it yourself and generalize it to support various scenarios.
Here is an example implementation, but since I'm weak at writing algorithms, it can give you an idea of reimplementing it in a much better manner.
final class Split {
private Split() {
}
static <E, R, K, V> R split(final Iterator<E> iterator, final Splitter<E, R, K, V> splitter) {
final R outerResult = splitter.newResult();
while ( iterator.hasNext() ) {
final E e = iterator.next();
doSplit(splitter, outerResult, e);
}
return outerResult;
}
static <E, R, K, V> R split(final Iterable<E> iterable, final Splitter<E, R, K, V> splitter) {
final R outerResult = splitter.newResult();
for ( final E e : iterable ) {
doSplit(splitter, outerResult, e);
}
return outerResult;
}
static <E, R, K, V> Collector<E, ?, R> asCollector(final Splitter<E, R, K, V> splitter) {
return Collector.of(
splitter::newResult,
(outerResult, e) -> doSplit(splitter, outerResult, e),
(r1, r2) -> {
throw new UnsupportedOperationException();
},
Function.identity()
);
}
private static <E, R, K, V> void doSplit(final Splitter<E, R, K, V> splitter, final R outerResult, final E e) {
final K elementKey = splitter.elementToKey(e);
final K[] keyGroup = splitter.keyToKeyGroup(elementKey);
R result = outerResult;
final int lastI = keyGroup.length - 1;
for ( int i = 0; i < lastI; i++ ) {
final K innerKey = keyGroup[i];
final R candidateInnerResult = splitter.fromInnerResult(result, innerKey);
if ( candidateInnerResult == null ) {
final R newTargetResult = splitter.newResult();
#SuppressWarnings("unchecked")
final V castNewTargetResult = (V) newTargetResult;
splitter.toInnerResult(result, innerKey, castNewTargetResult);
result = newTargetResult;
} else {
result = candidateInnerResult;
}
}
final V value = splitter.elementToValue(e);
splitter.toInnerResult(result, keyGroup[lastI], value);
}
}
In general, doSplit(...) tries to "split" keys for any sequence of elements, hence you can split anything, not just maps.
Here is the interface that is used above.
All of its methods are used in doSplit(...) to do different tasks.
// E - type of elements it can process
// R - the result object type
// K - key type
// K - value type
interface Splitter<E, R, K, V> {
// A factory method to create the outer of an inner result
R newResult();
// A method to extract a key from the element
K elementToKey(E element);
// A method to extract a value from the element
V elementToValue(E element);
// A method to split a key to a key group so we can have a nested objects identitied with
K[] keyToKeyGroup(K key);
// A method to extract an inner result from existing inner result
R fromInnerResult(R innerResult, K innerKey);
// A method to put a key/value pair to the result
void toInnerResult(R innerResult, K innerKey, V value);
// A convenience method similar to Collector.of
static <E, R, K, V> Splitter<E, R, K, V> of(
final Function<? super E, ? extends K> elementToKey,
final Function<? super E, ? extends V> elementToValue,
final Function<? super K, ? extends K[]> keyToKeyGroup,
final Supplier<? extends R> newResult,
final BiFunction<? super R, ? super K, ? extends R> fromInnerResult,
final TriConsumer<? super R, ? super K, ? super V> toInnerResult
) {
return new Splitter<E, R, K, V>() {
#Override
public R newResult() {
return newResult.get();
}
#Override
public K elementToKey(final E element) {
return elementToKey.apply(element);
}
#Override
public V elementToValue(final E element) {
return elementToValue.apply(element);
}
#Override
public K[] keyToKeyGroup(final K key) {
return keyToKeyGroup.apply(key);
}
#Override
public R fromInnerResult(final R innerResult, final K innerKey) {
return fromInnerResult.apply(innerResult, innerKey);
}
#Override
public void toInnerResult(final R innerResult, final K innerKey, final V value) {
toInnerResult.accept(innerResult, innerKey, value);
}
};
}
}
And since there is no a tri-consumer in Java 8:
interface TriConsumer<T, U, V> {
void accept(T t, U u, V v);
}
Now, since this is a generic approach, you can have multiple implementations.
For example, a splitter that can split the sequence to a map:
final class MapSplitters {
private MapSplitters() {
}
static <K, V> Splitter<Map.Entry<K, V>, Map<K, V>, K, V> of(final Function<? super K, ? extends K[]> keyToKeyGroup) {
return of(keyToKeyGroup, LinkedTreeMap::new);
}
static <K, V> Splitter<Map.Entry<K, V>, Map<K, V>, K, V> of(final Function<? super K, ? extends K[]> keyToKeyGroup,
final Supplier<? extends Map<K, V>> mapFactory) {
return Splitter.of(
Map.Entry::getKey,
Map.Entry::getValue,
keyToKeyGroup, mapFactory, (innerMap, key) -> {
#SuppressWarnings("unchecked")
final Map<K, V> castInnerMap = (Map<K, V>) innerMap.get(key);
return castInnerMap;
},
Map::put
);
}
}
Or to Gson JsonElement, JsonObject in particular:
final class JsonElementSplitters {
private JsonElementSplitters() {
}
static <V> Splitter<Map.Entry<String, V>, JsonObject, String, V> of(final Function<? super String, ? extends String[]> keyToKeyGroup) {
return of(keyToKeyGroup, JsonElementSplitters::simpleObjectToSimpleJsonElement);
}
static <V> Splitter<Map.Entry<String, V>, JsonObject, String, V> of(final Function<? super String, ? extends String[]> keyToKeyGroup,
final Gson gson) {
return of(keyToKeyGroup, gson::toJsonTree);
}
static <V> Splitter<Map.Entry<String, V>, JsonObject, String, V> of(final Function<? super String, ? extends String[]> keyToKeyGroup,
final Function<? super V, ? extends JsonElement> valueToJsonElement) {
return Splitter.of(
Map.Entry::getKey,
Map.Entry::getValue, keyToKeyGroup,
JsonObject::new,
(innerJsonObject, key) -> {
final JsonElement jsonElement = innerJsonObject.get(key);
return jsonElement != null ? jsonElement.getAsJsonObject() : null;
},
(jsonObject, property, value) -> jsonObject.add(property, valueToJsonElement.apply(value))
);
}
// In simple cases we can do a primitive box value to a simple JSON value
private static JsonElement simpleObjectToSimpleJsonElement(final Object o) {
if ( o == null ) {
return JsonNull.INSTANCE;
}
if ( o instanceof JsonElement ) {
return (JsonElement) o;
}
if ( o instanceof Boolean ) {
return new JsonPrimitive((Boolean) o);
}
if ( o instanceof Number ) {
return new JsonPrimitive((Number) o);
}
if ( o instanceof String ) {
return new JsonPrimitive((String) o);
}
if ( o instanceof Character ) {
return new JsonPrimitive((Character) o);
}
throw new IllegalArgumentException("Cannot convert " + o.getClass());
}
}
Example of use:
private static final Pattern dotPattern = Pattern.compile("\\.");
public static void main(final String... args) {
final Map<String, Object> map = ImmutableMap.of("stackoverflow.questions.value", "value");
final Splitter<Map.Entry<String, Object>, Map<String, Object>, String, Object> toMapSplitter = MapSplitters.of(dotPattern::split);
final Splitter<Map.Entry<String, Object>, JsonObject, String, Object> toJsonObjectSplitter = JsonElementSplitters.of(dotPattern::split);
// A simple to-inner-maps split example
System.out.println(Split.split(map.entrySet(), toMapSplitter));
// A simple to-nested-JSON-objects split example
System.out.println(Split.split(map.entrySet(), toJsonObjectSplitter));
// Or even use it with Java 8 Stream API
System.out.println(
map.entrySet()
.stream()
.map(e -> new AbstractMap.SimpleImmutableEntry<>(e.getKey().toUpperCase(), e.getValue()))
.collect(Split.asCollector(toMapSplitter))
);
}
Output:
{stackoverflow={questions={value=value}}}
{"stackoverflow":{"questions":{"value":"value"}}}
{STACKOVERFLOW={QUESTIONS={VALUE=value}}}
I'm not sure if any of built-in Java 8 Collectors can do this, but it looks something like a grouping collector.
Currently I'm developing a merge between two sorted collections of elements of type T (the type is not important as long you provide a means to compare the Type, for example, in Java, A Comparator<T> will do the work).
What I don't want is to necessarily merge both data structures involved in the merge process (I don't want to get an entire new structure holding both elements merged). What I want is to have some kind of observer of the merge process in order to define what to do with each merged element in another class. For example, a would like to have something like this:
merger.merge(leftCollection,rightCollection,theComparator,theObserver).
Where the observer is a object watching the merge algorithm and gets notified of the actions, i mean :
interface MergeObserver<T> {
/**
* Triggered when the merge algorithm decides to merge only the left entry.
* This case correspond to the case when there is no equivalent entry on the right collection.
*/
public void mergeLeft(T entry);
/**
* Triggered when the merge algorithm decides to merge both entries.
* This case correspond to the case when there exists the same entry on both collections.
*/
public void mergeBoth(T left, T right);
/**
* Triggered when the merge algorithm decides to merge only the right entry.
* This case correspond to the case when there is no equivalent entry on the left collection.
*/
public void mergeRight(T entry);
}
I have already make my implementation for sorted collections, but... I would like to share this feeling, and here comes the question, about if someone has thought of this before, specially in Guava Libraries, and what are the proper terminology employed.
The two most commonly used patterns for separating the traversal of a data structure and the processing of the data are the visitor pattern and the iterator pattern. Both of those patterns can be applied not only to real data structures that are present in memory but also to "virtual" data structures (which is probably not the proper term). e.g. the method List.subList in the Java API creates a view of a part of the list. So the List object returned by it is just a reference to part of another lists data. Of course you can also combine data structures. You could for example have a method that takes as arguments two iterators and returns a new iterator that merges the two without using any additional memory because that merged list is not actually present in RAM.
If you used Scala instead of Java you would have lots of methods available that can transform iterators in many different ways to achieve effects like this.
import java.util.function.Predicate;
import java.util.function.BiPredicate;
import java.util.function.Supplier;
import java.util.function.Consumer;
import java.util.stream.IntStream;
import java.util.NoSuchElementException;
interface MyIterator<T> extends Iterator<T> {
class Peekable<T> {
private final MyIterator<T> iter;
private T next = null;
private boolean isNextBuffered = false;
private boolean atEnd = false;
private Peekable(MyIterator<T> iter) {
this.iter = iter;
}
private void advance() {
if(atEnd) throw new NoSuchElementException();
if(iter.hasNext()) {
next = iter.next();
isNextBuffered = true;
} else {
atEnd = true;
}
}
private boolean hasNext() {
if(atEnd) return false;
if(!isNextBuffered) advance();
return !atEnd;
}
private T next() {
T next = peek();
advance();
return next;
}
private T peek() {
if(hasNext()) return next;
throw new NoSuchElementException();
}
}
static <T> MyIterator<T> of(BooleanSupplier hasNext, Supplier<T> next) {
return new MyIterator<T>() {
public boolean hasNext() {
return hasNext.getAsBoolean();
}
public T next() {
return next.get();
}
};
}
static <T> MyIterator<T> of(Iterator<T> iter) {
return of(iter::hasNext, iter::next);
}
static MyIterator<Integer> range(int start, int end) {
int[] value = {start};
return of(() -> value[0] < end, () -> value[0]++);
}
default <R> MyIterator<R> map(Function<? super T,? extends R> mapper) {
return of(this::hasNext, () -> mapper.apply(this.next()));
}
default MyIterator<T> filter(Predicate<? super T> predicate) {
Peekable<T> iter = new Peekable<T>(this);
return new MyIterator<T>() {
public boolean hasNext() {
while(iter.hasNext() && !predicate.test(iter.peek())) iter.advance();
return iter.hasNext();
}
public T next() {
hasNext();
return iter.next();
}
};
}
default MyIterator<T> merge(MyIterator<T> other, BiPredicate<? super T,? super T> smallerEqual) {
Peekable<T> iter1 = new Peekable<T>(this);
Peekable<T> iter2 = new Peekable<T>(other);
return of(() -> iter1.hasNext() || iter2.hasNext(),
() -> {
if(!iter1.hasNext()) return iter2.next();
else if(!iter2.hasNext()) return iter1.next();
else {
T elem1 = iter1.peek();
T elem2 = iter2.peek();
return smallerEqual.test(elem1, elem2) ? iter1.next() : iter2.next();
}
});
}
}
interface MyIterable<T> extends Iterable<T> {
default Iterator<T> iterator() {
return myIterator();
}
MyIterator<T> myIterator();
static <T> MyIterable<T> of(Supplier<MyIterator<T>> myIterator) {
return new MyIterable<T>() {
public MyIterator<T> myIterator() {
return myIterator.get();
}
};
}
static <T> MyIterable<T> of(Iterable<T> iterable) {
return of(() -> MyIterator.of(iterable.iterator()));
}
static MyIterable<Integer> range(int start, int end) {
return of(() -> MyIterator.range(start, end));
}
default <R> MyIterable<R> map(Function<? super T,? extends R> mapper) {
return of(() -> this.myIterator().map(mapper));
}
default MyIterable<T> filter(Predicate<? super T> predicate) {
return of(() -> this.myIterator().filter(predicate));
}
default MyIterable<T> merge(MyIterable<T> other, BiPredicate<? super T,? super T> smallerEqual) {
return of(() -> this.myIterator().merge(other.myIterator(), smallerEqual));
}
}
public class Test {
public static void main(String[] args) {
MyIterable<Integer> iterable = MyIterable.range(0, 10);
MyIterable<Integer> iter1 = iterable.map(x -> 2 * x).filter(x -> x < 10);
MyIterable<Integer> iter2 = iterable.map(x -> 2 * x + 1).filter(x -> x < 10);
MyIterable<Integer> iterMerged = iter1.merge(iter2, (x, y) -> x <= y);
iter1.forEach(System.out::println);
System.out.println();
iter2.forEach(System.out::println);
System.out.println();
iterMerged.forEach(System.out::println);
}
}
What would probably be more idiomatically "java" is to write your merger with a listener:
public interface Merger {
public Collection<T> merge(Collection<T> left, Collection<T> right, Comparator comparator);
public void addListener(Observer observer);
public void notifyListener(Message message);
}
public interface Observer {
public void notify(Message message);
}
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.
How can I construct a SortedMap on top of Guava's computing map (or vice versa)? I want the sorted map keys as well as computing values on-the-fly.
The simplest is probably to use a ConcurrentSkipListMap and the memoizer idiom (see JCiP), rather than relying on the pre-built unsorted types from MapMaker. An example that you could use as a basis is a decorator implementation.
May be you can do something like this.It's not a complete implementation.Just a sample to convey the idea.
public class SortedComputingMap<K, V> extends TreeMap<K, V> {
private Function<K, V> function;
private int maxSize;
public SortedComputingMap(int maxSize, Function<K, V> function) {
this.function = function;
this.maxSize = maxSize;
}
#Override
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
#Override
public void putAll(Map<? extends K, ? extends V> map) {
throw new UnsupportedOperationException();
}
#Override
public V get(Object key) {
V tmp = null;
K Key = (K) key;
if ((tmp = super.get(key)) == null) {
super.put(Key, function.apply(Key));
}
if (size() > maxSize)
pollFirstEntry();
return tmp;
}
public static void main(String[] args) {
Map<Integer, Long> sortedMap = new SortedComputingMap<Integer, Long>(3,
new Function<Integer, Long>() {
#Override
public Long apply(Integer n) {
Long fact = 1l;
while (n != 0)
fact *= n--;
return fact;
}
});
sortedMap.get(12);
sortedMap.get(1);
sortedMap.get(2);
sortedMap.get(5);
System.out.println(sortedMap.entrySet());
}
}
If you need the thread safety, this could be tricky, but if you don't I'd recommend something close to Emil's suggestion, but using a ForwardingSortedMap rather than extending TreeMap directly.
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