JEP 186: Collection Literals
Peter Levart
peter.levart at gmail.com
Sat Jan 18 08:38:14 PST 2014
On 01/18/2014 02:55 PM, Zhong Yu wrote:
> On Sat, Jan 18, 2014 at 5:16 AM, Peter Levart <peter.levart at gmail.com> wrote:
>> 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
> Is this guaranteed? i.e. the same lambda object will behave the same
> over the time?
No, it's just something that usually holds. If lambda behaves as pure
function and is given the same inputs, it must produce the same result.
In this example it is producing a side-effect, and that is a kind of
result - the same sequence of call-backs, each time it is invoked...
This assumption can easily be violated if lambda accesses any "static"
state that changes over time, which doesn't make it capturing lambda by
itself, but can cause different results in each invocation. For example:
List<Double> l = immutableList(b -> b._(ThreadLocalRandom.current().nextDouble()));
Regards, Peter
>> 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