java - 不可变链表的拆分器

标签 java linked-list java-8 singly-linked-list spliterator

这是不可变链表的经典实现:

public abstract class List<A> implements Iterable<A> {
    private static final List NIL = new Nil();

    public abstract A head();
    public abstract List<A> tail();
    public List<A> cons(A a) { return new Cons<>(a, this); }

    public static <A> List<A> nil() { return NIL; }

    @Override
    public Iterator<A> iterator() {
        return new Iterator<A>() {
            private List<A> list = List.this;

            @Override
            public boolean hasNext() {
                return list != NIL;
            }

            @Override
            public A next() {
                A n = list.head();
                list = list.tail();
                return n;
            }
        };
    }

    public Stream<A> stream() {
        return StreamSupport.stream(spliterator(), false);
    }

    public Stream<A> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }
}

class Nil extends List {
    @Override public Object head() { throw new NoSuchElementException(); }
    @Override public List tail() { throw new NoSuchElementException(); }
}

class Cons<A> extends List<A> {
    private final A head;
    private final List<A> tail;

    Cons(A head, List<A> tail) {
        this.head = head;
        this.tail = tail;
    }

    @Override public A head() { return head; }
    @Override public List<A> tail() { return tail; }
}

spliterator() 的默认实现不支持高效的并行化:

List<Integer> list = List.<Integer> nil().cons(3).cons(2).cons(1);

list.parallelStream().forEach(i -> {
    System.out.println(i);
    try {
        Thread.sleep(1000);
    } catch (Exception e) {
        e.printStackTrace();
    }
});

这将按顺序打印 1, 2, 3

如何实现spliterator()来支持高效的并行化?

最佳答案

无法报告估计大小(这是 Iterable 的默认实现)的拆分器很难被并行管道拆分。如果跟踪 List 的大小,则可以解决此问题。在您的情况下,跟踪确切大小并不难:

public abstract class List<A> implements Iterable<A> {
    ...
    public abstract long size();

    @Override
    public Spliterator<A> spliterator() {
        return Spliterators.spliterator(iterator(), size(), Spliterator.ORDERED);
    }
}

class Nil extends List {
    ...
    public long size() {
        return 0;
    }
}

class Cons<A> extends List<A> {
    ...
    private final long size;

    Cons(A head, List<A> tail) {
        this.head = head;
        this.tail = tail;
        this.size = tail.size()+1;
    }

    ...

    @Override
    public long size() {
        return size;
    }
}

在那之后并行化会更好。请注意,它仍然是 poor man parallelization,因为你不能快速跳到列表的中间,但在许多情况下它会提供合理的加速。

另请注意,最好明确指定 Spliterator.ORDERED 特性。否则,在并行流操作中可能会忽略顺序,即使它是明确请求的(例如,通过 forEachOrdered() 终端操作)。

关于java - 不可变链表的拆分器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32044894/

相关文章:

java - 链表 - 迭代添加方法 - 无 getter 和 setter

java - 如何减少链表中的搜索时间?

c - 生产者-消费者多线程段错误: core dump

java - 是否有支持 NIST 标准的随机生成器的 Java 8 实现?

java - 无法在android中处理json数据

java - 在 WildFly/JBoss 中设置 TCCL

java - 使用点符号的 Thymeleaf 3 map 访问

java - 找出谁调用了 jvm shutdown hook

java - Java流中continue关键字的等价物?

java - 如何使用新的 1.8 流 API 连接字符串