JEP 186: Collection Literals

Peter Levart peter.levart at gmail.com
Sat Jan 18 03:16:30 PST 2014


On 01/16/2014 12:43 AM, Remi Forax wrote:
> We can also use the Builder Pattern of Ruby now that we have a lambda syntax
>     new ArrayList<>(builder -> builder.add(1).add(2).add(3));
>
> Rémi

This one is interesting, because it shows what can be achieved with 
lambdas today. It enables expressions that evaluate into singleton 
objects (like non-capturing lambdas). If the lambda body is 
non-capturing, it evaluates into constant singleton in current 
implementation. So objects produced with such lambdas can be cached 
(using lambda object as a weak key). Here's a sample test that exercises 
that:

public class Test {
     public static void main(String[] args) {
         for (int i = 0; i < 3; i++) {
             List<Double> list = immutableList(b -> b._(3.14)._(0.33333));
             System.out.println(list + " : " + 
System.identityHashCode(list));
         }
         System.out.println();
         for (int i = 0; i < 3; i++) {
             double x = i;
             List<Double> list = immutableList(b -> 
b._(3.14)._(0.33333)._(x));
             System.out.println(list + " : " + 
System.identityHashCode(list));
         }
         System.out.println();
         for (int i = 0; i < 3; i++) {
             Map<String, Integer> map = immutableMap(b -> b._("aa", 
1)._("bb", 2)._("cc", 3));
             System.out.println(map + " : " + System.identityHashCode(map));
         }
         System.out.println();
         for (int i = 0; i < 3; i++) {
             int x = i;
             Map<String, Integer> map = immutableMap(b -> b._("aa", 
1)._("bb", 2)._("cc", x));
             System.out.println(map + " : " + System.identityHashCode(map));
         }
     }
}

which prints:

[3.14, 0.33333] : 1706377736
[3.14, 0.33333] : 1706377736
[3.14, 0.33333] : 1706377736

[3.14, 0.33333, 0.0] : 868693306
[3.14, 0.33333, 1.0] : 989110044
[3.14, 0.33333, 2.0] : 321001045

{aa=1, bb=2, cc=3} : 1044036744
{aa=1, bb=2, cc=3} : 1044036744
{aa=1, bb=2, cc=3} : 1044036744

{aa=1, bb=2, cc=0} : 1915318863
{aa=1, bb=2, cc=1} : 295530567
{aa=1, bb=2, cc=2} : 1324119927



Regards, Peter


P.S. Here's what I used to compile above test:

public interface Builder<T> extends Consumer<T> {
     default Builder<T> _(T t) {
         accept(t);
         return this;
     }
}

public interface BiBuilder<T, U> extends BiConsumer<T, U> {
     default BiBuilder<T, U> _(T t, U u) {
         accept(t, u);
         return this;
     }
}

public class Collections2 {

     public static <T> ArrayList<T> arrayList(Consumer<Builder<T>> 
producer) {
         ArrayList<T> list = new ArrayList<>();
         producer.accept(list::add);
         return list;
     }

     // cache of immutable lists per producer
     private static final Map<Consumer<? extends Builder<?>>, List<?>> 
INTERNED_IMMUTABLE_LISTS = new WeakHashMap<>();

     public static <T> List<T> immutableList(Consumer<Builder<T>> 
producer) {
         synchronized (INTERNED_IMMUTABLE_LISTS) {
             // check if already interned
             @SuppressWarnings("unchecked")
             List<T> list = (List<T>) 
INTERNED_IMMUTABLE_LISTS.get(producer);
             if (list != null) return list;
         }

         // count elements produced
         final int[] count = new int[1];
         producer.accept(t -> count[0]++);

         // construct new list
         ArrayList<T> aList = new ArrayList<>(count[0]);
         producer.accept(aList::add);

         synchronized (INTERNED_IMMUTABLE_LISTS) {
             // recheck (highly unlikely)
             @SuppressWarnings("unchecked")
             List<T> list = (List<T>) 
INTERNED_IMMUTABLE_LISTS.get(producer);
             if (list != null) return list;
             // put it into cache and return
             list = Collections.unmodifiableList(aList);
             INTERNED_IMMUTABLE_LISTS.put(producer, list);
             return list;
         }
     }

     public static <K, V> HashMap<K, V> hashMap(Consumer<BiBuilder<K, 
V>> producer) {
         HashMap<K, V> map = new HashMap<>();
         producer.accept(map::put);
         return map;
     }

     // cache of immutable maps per producer
     private static final Map<Consumer<? extends BiBuilder<?, ?>>, 
Map<?, ?>> INTERNED_IMMUTABLE_MAPS = new WeakHashMap<>();

     public static <K, V> Map<K, V> immutableMap(Consumer<BiBuilder<K, 
V>> producer) {
         synchronized (INTERNED_IMMUTABLE_MAPS) {
             // check if already interned
             @SuppressWarnings("unchecked")
             Map<K, V> map = (Map<K, V>) 
INTERNED_IMMUTABLE_MAPS.get(producer);
             if (map != null) return map;
         }

         // count elements produced
         final int[] count = new int[1];
         producer.accept((k, v) -> count[0]++);

         // construct new map
         HashMap<K, V> hMap = new HashMap<>(count[0] * 4 / 3);
         producer.accept(hMap::put);

         synchronized (INTERNED_IMMUTABLE_MAPS) {
             // recheck (highly unlikely)
             @SuppressWarnings("unchecked")
             Map<K, V> map = (Map<K, V>) 
INTERNED_IMMUTABLE_MAPS.get(producer);
             if (map != null) return map;
             // put it into cache and return
             map = Collections.unmodifiableMap(hMap);
             INTERNED_IMMUTABLE_MAPS.put(producer, map);
             return map;
         }
     }
}




More information about the lambda-dev mailing list