Add lazyMap versions that use a Transformer as their factory.

These are in fact incredibly powerful, especially if wrapped in a synchronized wrapper.
In this case it forms a self-populating cache.
Idea by Dennis Mitchell,  bugzilla ref 17953


git-svn-id: https://svn.apache.org/repos/asf/jakarta/commons/proper/collections/trunk@130994 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stephen Colebourne 2003-04-06 20:07:55 +00:00
parent 7f942e98b6
commit af6d9f164f
2 changed files with 197 additions and 21 deletions

View File

@ -1,7 +1,7 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/MapUtils.java,v 1.18 2003/04/06 19:32:18 scolebourne Exp $
* $Revision: 1.18 $
* $Date: 2003/04/06 19:32:18 $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/java/org/apache/commons/collections/MapUtils.java,v 1.19 2003/04/06 20:07:55 scolebourne Exp $
* $Revision: 1.19 $
* $Date: 2003/04/06 20:07:55 $
*
* ====================================================================
*
@ -963,6 +963,37 @@ public class MapUtils {
}
/**
* Implementation of a map that creates objects on demand.
*/
static class LazyTransformerMap
extends ProxyMap {
protected final Transformer transformer;
public LazyTransformerMap(Map map, Transformer transformer) {
super(map);
if (map == null) {
throw new IllegalArgumentException("Map must not be null");
}
if (transformer == null) {
throw new IllegalArgumentException("Transformer must not be null");
}
this.transformer = transformer;
}
public Object get(Object key) {
if (!map.containsKey(key)) {
Object value = transformer.transform(key);
map.put(key, value);
return value;
}
return map.get(key);
}
}
/**
* Implementation of a sorted map that checks additions.
*/
@ -978,17 +1009,14 @@ public class MapUtils {
return getSortedMap().firstKey();
}
public Object lastKey() {
return getSortedMap().lastKey();
}
public Comparator comparator() {
return getSortedMap().comparator();
}
public SortedMap subMap(Object o1, Object o2) {
SortedMap sub = getSortedMap().subMap(o1, o2);
return new PredicatedSortedMap(sub, keyPredicate, valuePredicate);
@ -1025,17 +1053,14 @@ public class MapUtils {
return getSortedMap().firstKey();
}
public Object lastKey() {
return getSortedMap().lastKey();
}
public Comparator comparator() {
return getSortedMap().comparator();
}
public SortedMap subMap(Object o1, Object o2) {
return new FixedSizeSortedMap(getSortedMap().subMap(o1, o2));
}
@ -1069,17 +1094,14 @@ public class MapUtils {
return getSortedMap().firstKey();
}
public Object lastKey() {
return getSortedMap().lastKey();
}
public Comparator comparator() {
return getSortedMap().comparator();
}
public SortedMap subMap(Object o1, Object o2) {
return new LazySortedMap(getSortedMap().subMap(o1, o2), factory);
}
@ -1098,6 +1120,47 @@ public class MapUtils {
}
/**
* Implementation of a sorted map that creates objects on demand.
*/
static class LazyTransformerSortedMap
extends LazyTransformerMap
implements SortedMap {
public LazyTransformerSortedMap(SortedMap m, Transformer transformer) {
super(m, transformer);
}
public Object firstKey() {
return getSortedMap().firstKey();
}
public Object lastKey() {
return getSortedMap().lastKey();
}
public Comparator comparator() {
return getSortedMap().comparator();
}
public SortedMap subMap(Object o1, Object o2) {
return new LazyTransformerSortedMap(getSortedMap().subMap(o1, o2), transformer);
}
public SortedMap headMap(Object o1) {
return new LazyTransformerSortedMap(getSortedMap().headMap(o1), transformer);
}
public SortedMap tailMap(Object o1) {
return new LazyTransformerSortedMap(getSortedMap().tailMap(o1), transformer);
}
private SortedMap getSortedMap() {
return (SortedMap)map;
}
}
//-----------------------------------------------------------------------
/**
* Returns a synchronized map backed by the given map.
@ -1187,7 +1250,7 @@ public class MapUtils {
}
/**
* Returns a "lazy" map whose values will be created on demand.<P>
* Returns a "lazy" map whose values will be created on demand.
* <p>
* When the key passed to the returned map's {@link Map#get(Object)}
* method is not present in the map, then the factory will be used
@ -1195,20 +1258,19 @@ public class MapUtils {
* associated with that key.
* <p>
* For instance:
*
* <pre>
* Factory factory = new Factory() {
* public Object create() {
* return new Date();
* }
* }
* Map lazy = MapUtils.lazyMap(new HashMap(), factory);
* Object obj = lazy.get("test");
* Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
* Object obj = lazyMap.get("test");
* </pre>
*
* After the above code is executed, <code>obj</code> will contain
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
* instance is the value for the <code>test</code> key.
* instance is the value for the <code>"test"</code> key in the map.
*
* @param map the map to make lazy, must not be null
* @param factory the factory for creating new objects, must not be null
@ -1219,6 +1281,45 @@ public class MapUtils {
return new LazyMap(map, factory);
}
/**
* Returns a "lazy" map whose values will be created on demand.
* <p>
* When the key passed to the returned map's {@link Map#get(Object)}
* method is not present in the map, then the factory will be used
* to create a new object and that object will become the value
* associated with that key. The factory is a {@link Transformer}
* that will be passed the key which it must transform into the value.
* <p>
* For instance:
* <pre>
* Transformer factory = new Transformer() {
* public Object transform(Object mapKey) {
* return new File(mapKey);
* }
* }
* Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
* Object obj = lazyMap.get("C:/dev");
* </pre>
*
* After the above code is executed, <code>obj</code> will contain
* a new <code>File</code> instance for the C drive dev directory.
* Furthermore, that <code>File</code> instance is the value for the
* <code>"C:/dev"</code> key in the map.
* <p>
* If a lazy map is wrapped by a synchronized map, the result is a simple
* synchronized cache. When an object is not is the cache, the cache itself
* calls back to the factory Transformer to populate itself, all within the
* same synchronized block.
*
* @param map the map to make lazy, must not be null
* @param transformerFactory the factory for creating new objects, must not be null
* @return a lazy map backed by the given map
* @throws IllegalArgumentException if the Map or Transformer is null
*/
public static Map lazyMap(Map map, Transformer transformerFactory) {
return new LazyTransformerMap(map, transformerFactory);
}
//-----------------------------------------------------------------------
/**
* Returns a synchronized sorted map backed by the given sorted map.
@ -1329,7 +1430,7 @@ public class MapUtils {
*
* After the above code is executed, <code>obj</code> will contain
* a new <code>Date</code> instance. Furthermore, that <code>Date</code>
* instance is the value for the <code>test</code> key.
* instance is the value for the <code>"test"</code> key.
*
* @param map the map to make lazy, must not be null
* @param factory the factory for creating new objects, must not be null
@ -1340,4 +1441,43 @@ public class MapUtils {
return new LazySortedMap(map, factory);
}
/**
* Returns a "lazy" sorted map whose values will be created on demand.
* <p>
* When the key passed to the returned map's {@link Map#get(Object)}
* method is not present in the map, then the factory will be used
* to create a new object and that object will become the value
* associated with that key. The factory is a {@link Transformer}
* that will be passed the key which it must transform into the value.
* <p>
* For instance:
* <pre>
* Transformer factory = new Transformer() {
* public Object transform(Object mapKey) {
* return new File(mapKey);
* }
* }
* Map lazyMap = MapUtils.lazyMap(new HashMap(), factory);
* Object obj = lazyMap.get("C:/dev");
* </pre>
*
* After the above code is executed, <code>obj</code> will contain
* a new <code>File</code> instance for the C drive dev directory.
* Furthermore, that <code>File</code> instance is the value for the
* <code>"C:/dev"</code> key in the map.
* <p>
* If a lazy map is wrapped by a synchronized map, the result is a simple
* synchronized cache. When an object is not is the cache, the cache itself
* calls back to the factory Transformer to populate itself, all within the
* same synchronized block.
*
* @param map the map to make lazy, must not be null
* @param transformerFactory the factory for creating new objects, must not be null
* @return a lazy map backed by the given map
* @throws IllegalArgumentException if the Map or Transformer is null
*/
public static SortedMap lazySortedMap(SortedMap map, Transformer transformerFactory) {
return new LazyTransformerSortedMap(map, transformerFactory);
}
}

View File

@ -1,7 +1,7 @@
/*
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestMapUtils.java,v 1.3 2003/04/04 22:22:28 scolebourne Exp $
* $Revision: 1.3 $
* $Date: 2003/04/04 22:22:28 $
* $Header: /home/jerenkrantz/tmp/commons/commons-convert/cvs/home/cvs/jakarta-commons//collections/src/test/org/apache/commons/collections/TestMapUtils.java,v 1.4 2003/04/06 20:07:55 scolebourne Exp $
* $Revision: 1.4 $
* $Date: 2003/04/06 20:07:55 $
*
* ====================================================================
*
@ -213,5 +213,41 @@ public class TestMapUtils extends BulkTest {
};
}
public void testLazyMapFactory() {
Map map = MapUtils.lazyMap(new HashMap(), new Factory() {
public Object create() {
return new Integer(5);
}
});
assertEquals(0, map.size());
Integer i1 = (Integer) map.get("Five");
assertEquals(new Integer(5), i1);
assertEquals(1, map.size());
Integer i2 = (Integer) map.get(new String(new char[] {'F','i','v','e'}));
assertEquals(new Integer(5), i2);
assertEquals(1, map.size());
assertSame(i1, i2);
}
public void testLazyMapTransformer() {
Map map = MapUtils.lazyMap(new HashMap(), new Transformer() {
public Object transform(Object mapKey) {
if (mapKey instanceof String) {
return new Integer((String) mapKey);
}
return null;
}
});
assertEquals(0, map.size());
Integer i1 = (Integer) map.get("5");
assertEquals(new Integer(5), i1);
assertEquals(1, map.size());
Integer i2 = (Integer) map.get(new String(new char[] {'5'}));
assertEquals(new Integer(5), i2);
assertEquals(1, map.size());
assertSame(i1, i2);
}
}