Java 不可变链表的拆分器
这是不可变链表的经典实现:Java 不可变链表的拆分器,java,linked-list,java-8,singly-linked-list,spliterator,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 C
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; }
}
这将按顺序打印1、2、3
如何实现
spliterator()
以支持有效的并行化?您可以使用一些交错算法,例如,计算元素数并在整数除法后使用余数。这可以分割元素进行并行迭代
您也可以在构建迭代器之前进行一次迭代,将列表拆分为多个间隔,但这将超出流的目的-例如,如果您将其用于anyMatch
,它将大大降低您的速度
没有真正有效的方法来拆分链表(在少于线性时间内),除非您创建自己的链表实现,其中包含一些附加信息
编辑:哦,等等-您只能实现
Iterable
。这是相当有限的,你必须想出一个只有一次通过的算法。这意味着,拆分本身根本不会是并行的,因此您不妨在流程中的其他地方强制执行您的并行性。即使无法报告估计大小(这是Iterable的默认实现)的拆分器也无法通过并行管道进行很好的拆分。如果跟踪列表的大小,可以解决此问题。在您的情况下,跟踪准确的尺寸并不困难:
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;
}
}
公共抽象类列表实现Iterable{
...
公共抽象长尺寸();
@凌驾
公共拆分器拆分器(){
返回Spliterators.spliterator(迭代器(),size(),spliterator.ORDERED);
}
}
类Nil扩展列表{
...
公共长码(){
返回0;
}
}
类Cons扩展列表{
...
私人最终长尺寸;
缺点(头、尾){
这个头=头;
this.tail=tail;
this.size=tail.size()+1;
}
...
@凌驾
公共长码(){
返回大小;
}
}
在那之后,并行化将工作得更好。请注意,它仍然是穷人的并行化,因为您无法快速跳转到列表的中间,但在许多情况下,它将提供合理的加速
还要注意,最好明确指定拆分器.ORDERED
特性。否则,在并行流操作中,即使是显式请求的顺序(例如,通过forEachOrdered()
terminal操作),也可能会忽略顺序。考虑一下这篇文章:与可以实现SkipList数据结构的列表不同,这些数据结构更易于并行化。您计划对每个元素做多少工作?只有当它的重量大于100µs时,才有必要将其拆分为单个元素。您要问的是如何使用数据结构,这通常对大量元素都是有害的,同时还有一个只与大量元素相关的功能。不要惊讶这并没有真正得到回报。链接列表在教室里很好,但我在实践中从未见过任何实际的使用案例。@Holger我不确定平行性与集合中元素的数量有什么关系。如果我想并行处理3项繁重的任务,该怎么办?
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;
}
}