Lazy lists?

Paul Sandoz paul.sandoz at oracle.com
Wed Oct 9 03:22:05 PDT 2013


Hi Milles,

We avoided adding operation-based methods to collections themselves which would have made them lazy due to, in part, the complexity that it introduced to the implementation. So rather than a lazy data structure we opted for different abstraction we call Stream.

The approach of the specific example you present is falling into similar traps as discussed in [1] on trial division, and as such it is, IMHO, not a particular compelling reasons why lazy lists are needed.

Note: we are missing some operations on stream, such as limitWhen (or takeWhile) that would be useful for the trial division use-case.

Here is another way:

        Map<Integer, LinkedList<Integer>> m = new TreeMap<>();
        Function<Integer, LinkedList<Integer>> newList = (k) -> new LinkedList();
        IntStream.range(2, 100).forEach(x -> {
            if (!m.containsKey(x)) {
                // Always absent, just shorter expression
                m.computeIfAbsent(x * x, newList).push(x);
            }
            else {
                m.remove(x).forEach(p -> m.computeIfAbsent(x + p, newList).push(p));
            }
        });
        List<Integer> primes = m.values().stream().flatMap(List::stream).collect(toList());
        System.out.println(primes);

Obviously that is not parallelizable and of course it is not lazy, but then Java is not Haskell (for better or worse depending on one's opinion).

I think there are far more compelling reasons to add persistent collections to Java.

Paul.

[1] http://www.cs.hmc.edu/~oneill/papers/Sieve-JFP.pdf


On Oct 9, 2013, at 9:00 AM, "Millies, Sebastian" <Sebastian.Millies at softwareag.com> wrote:

> Hello there,
> 
> now that we have infinite Streams, wouldn’t it be worthwhile to have a lazy list
> implementation in the collection library? By Lazy list, I mean a list that
> would be strict for the head and lazy for the tail in that it
> 
> a) could be based on a Function<T> to generate the elements in the tail
>   in sequence (like Stream#iterate())
> b) would accept a Supplier<LazyList<T>> to supply a tail (like Stream#generate())
> c) would accept a Predicate<T> to filter out non-matching elements
>   from the tail
> d) would allow constant time access to the head.
> 
> It would stream() using a spliterator of unknown size.
> 
> With it, we could write code somewhat like this to generate the first 100 primes
> by trial division (where cons is also a static factory methof of LazyList):
> 
> LazyList<Integer> s = sieve(LazyList.newList(2, x -> x + 1));
> s.stream().limit(100).forEach(x->System.out.print(x + " "));
> 
> private LazyList <Integer> sieve(LazyList <Integer> s) {
>  Integer p = s.head();
>  return cons(p, () -> sieve(s.tail().filter(x -> x % p != 0)));
> }
> 
> Which would be pretty neat. Any particular reason – now that we have
> lambdas – that there are no lazy data structures in the library?
> 
> 
> n  Sebastian
> 
> Software AG – Sitz/Registered office: Uhlandstraße 12, 64297 Darmstadt, Germany – Registergericht/Commercial register: Darmstadt HRB 1562 - Vorstand/Management Board: Karl-Heinz Streibich (Vorsitzender/Chairman), Dr. Wolfram Jost, Arnd Zinnhardt; - Aufsichtsratsvorsitzender/Chairman of the Supervisory Board: Dr. Andreas Bereczky - http://www.softwareag.com
> 
> 



More information about the lambda-dev mailing list