39-案例实现
案例实现
根据如下步骤来实现本案例。
1.创建一个 Item
类来存储矩阵中每个元素的信息。它将有3个私有字段:一个名为 name
的字符型字段和两个名为 row
和 column
的整型字段。创建获取并设置这些字段值的方法。由于这个类的代码非常简单,所以在这里不会展示它。
2.创建一个 MySpliterator
类,指定它实现 Spliterator
接口。该类有4个属性:1个名为 items
的 Item
对象矩阵和3个名为 start
、 end
和 current
的整型字段,它们用于存储由此 Spliterator
接口处理的第一个和最后一个元素以及正在处理的当前元素。实现该类的构造方法来初始化所有这些字段:
public class MySpliterator implements Spliterator<Item> {
private Item[][] items;
private int start, end, current;
public MySpliterator(Item[][] items, int start, int end) {
this.items=items;
this.start=start;
this.end=end;
this.current=start;
}
3.实现 characteristics()
。该方法将返回一个描述 Spliterator
行为的 int
值。该值的含义将在后面的“结果分析”部分中进行介绍:
@Override
public int characteristics() {
return ORDERED | SIZED | SUBSIZED;
}
4.实现 estimateSize()
。该方法将返回此 Spliterator
要处理的元素数量。我们将计算 end
和 current
字段之间的差值作为返回值:
@Override
public long estimateSize() {
return end - current;
}
5.现在执行 tryAdvance()
。该方法将会用来尝试处理 Spliterator
的元素。 tryAdvance()
方法的输入参数是实现 Consumer
接口的对象。由于它将由Stream API调用,所以我们只需要关心它的实现。在本案例中,正如在本章的前面介绍中提到的,我们有一个 Item
对象的矩阵,它将每次处理一行数据,收到的消费者功能将处理一个 Item
对象。因此,如果 Spliterator
接口仍然有要处理的元素,则将使用 Consumer
函数接口的 accept()
方法来处理当前行的所有项:
@Override
public boolean tryAdvance(Consumer<? super Item> consumer) {
System.out.printf("MySpliterator.tryAdvance.start: %d, %d, %d\n",
start,end,current);
if (current < end) {
for (int i=0; i<items[current].length; i++) {
consumer.accept(items[current][i]);
}
current++;
System.out.printf("MySpliterator.tryAdvance.end:true\n");
return true;
}
System.out.printf("MySpliterator.tryAdvance.end:false\n");
return false;
}
6.现在执行 forEachRemaining()
。该方法将接收 Consumer
接口的实现,并将此功能应用于 Spliterator
的其余元素中。在本案例中,我们将为其余所有的元素调用 tryAdvance()
方法:
@Override
public void forEachRemaining(Consumer<? super Item> consumer) {
System.out.printf("MySpliterator.forEachRemaining.start\n");
boolean ret;
do {
ret=tryAdvance(consumer);
} while (ret);
System.out.printf("MySpliterator.forEachRemaining.end\n");
}
7.最后,实现 trySplit()
。该方法将由并行流所调用,由此将 Spliterator
分为两个子集。它将返回一个新的 Spliterator
对象,其中将包含由另一个线程来处理的元素。当前线程将会处理其余的元素。如果 Spliterator
对象不能分割,则必须返回一个 null
值。在本案例中,我们将在必须处理的元素中间计算元素。前半部分由当前线程处理,后半部分将另一个线程处理:
@Override
public Spliterator<Item> trySplit() {
System.out.printf("MySpliterator.trySplit.start\n");
if (end-start<=2) {
System.out.printf("MySpliterator.trySplit.end\n");
return null;
}
int mid=start+((end-start)/2);
int newStart=mid;
int newEnd=end;
end=mid;
System.out.printf("MySpliterator.trySplit.end: %d, %d, %d,
%d, %d, %d\n",start, mid, end, newStart,
newEnd, current);
return new MySpliterator(items, newStart, newEnd);
}
8.现在,实现本案例的 Main
类和 main()
方法。首先,用10行10列的 Item
对象声明和初始化一个矩阵:
public class Main {
public static void main(String[] args) {
Item[][] items;
items= new Item[10][10];
for (int i=0; i<10; i++) {
for (int j=0; j<10; j++) {
items[i][j]=new Item();
items[i][j].setRow(i);
items[i][j].setColumn(j);
items[i][j].setName("Item "+i+" "+j);
}
}
9.然后,创建一个 MySpliterator
对象来处理矩阵中的所有元素:
MySpliterator mySpliterator=new MySpliterator(items, 0,
items.length);
10.最后,用 StreamSupport
类的 stream()
方法从 Spliterator
上创建一个流。传递 true
值作为第二个参数来表示我们的流将要并行处理。然后,用 Stream
类的 forEach()
方法来编写每个元素的信息:
StreamSupport.stream(mySpliterator, true).forEach( item -> {
System.out.printf("%s: %s\n",Thread.currentThread()
.getName(),item.getName());
});
}