当前位置:嗨网首页>书籍在线阅读

39-案例实现

  
选择背景色: 黄橙 洋红 淡粉 水蓝 草绿 白色 选择字体: 宋体 黑体 微软雅黑 楷体 选择字体大小: 恢复默认

案例实现

根据如下步骤来实现本案例。

1.创建一个 Item 类来存储矩阵中每个元素的信息。它将有3个私有字段:一个名为 name 的字符型字段和两个名为 rowcolumn 的整型字段。创建获取并设置这些字段值的方法。由于这个类的代码非常简单,所以在这里不会展示它。

2.创建一个 MySpliterator 类,指定它实现 Spliterator 接口。该类有4个属性:1个名为 itemsItem 对象矩阵和3个名为 startendcurrent 的整型字段,它们用于存储由此 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 要处理的元素数量。我们将计算 endcurrent 字段之间的差值作为返回值:

@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());
  });
}