玩游戏学编程(1)-猜数字-Stream

在这一篇玩游戏学编程(1)-猜数字中出现了Stream相关技术, 先看看代码其中的一段代码:

1
2
3
4
5
6
7
8
9
10
this.tilePane = this.populateGrid();  
this.root.getChildren().set(0, tilePane);
//6秒后执行线程,计划任务
this.servicePool.schedule(
()->{
tilePane.getChildren()
.stream()
.map(n->(Tile)n)
.forEach(Tile::hide);
}, 6, TimeUnit.SECONDS);

代码中populateGrid()函数会返回一个Pane, 这个Pane中包含多个Tile, 每个Tile是一个矩形方框,其中还有一个数字, 那就意味着tilePane这个对象中有多个控件,我们可以通过循环或Stream遍历tilePane中的每一个控件;

this.servicePool.schedule(lambda表达式)它可以传递一个Lambda表达式, 这里可以改成传统的自定义线程类对象,只不过现在可以用Lambda表达式的方式简写, 因为线程中的Runnable接口也只有一个抽象方法, 传统的代码写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
//创建一个线程类,里边包含异步任务
class ScheduleThread implements Runnable {
@Override
public void run() {
ObservableList<Node> list = tilePane.getChildren();
for (int i = 0; i < list.size(); i++) {
TileView tv = (TileView)list.get(i);
tv.hide();
}
}
}
//创建一个异步任务,6秒后执行线程的run()方法
this.servicePool.schedule(new ScheduleThread(), 6, TimeUnit.SECONDS);

遍历可以采用以下方式:

1
2
3
4
5
6
7
8
9
//获取面板上的所有控件元素,返回的是一个集合
ObservableList<Node> list = tilePane.getChildren();
//遍历集合
for (int i = 0; i < list.size(); i++) {
//将集合中的每一个元素获取到,并强制转换成Tile对象
Tile tile = (TileView)list.get(i);
//调用控件元素的hide方法
tile.hide();
}

以上这种方案很传统,代码繁杂,如果采用Lambda和Stream的方式,代码更简洁:

1
2
3
4
5
this.servicePool.schedule( ()->{
tilePane.getChildren().stream().map(n->(Tile)n)
.forEach(Tile::hide);
},
6, TimeUnit.SECONDS);

下面我们再来举个例子, 假如我们有一个ArrayList集合,其中存放了一些数据,那么遍历这种集合有两种方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
List<String> fruitList = new ArrayList<String>();
fruitList.add("苹果");
fruitList.add("香蕉");
fruitList.add("西瓜");
//第一种遍历方式,每获取一个元素直接输出, 此种方式属于外部迭代
for (int i = 0; i < fruitList.size(); i++) {
String fruit = fruitList.get(i);
System.out.println(fruit);
}
//另一种方式,采用forEach,其实也是外部迭代,只是采用了Lambda表达式
fruitList.forEach(item->{
System.out.println(item);
});
//如果仅仅是输出集合中的每一个元素,还可以使用更简单的写法
fruitList.forEach(System.out::println);

我们可以使用Stream对这个集合进行过滤、筛选、切片、映射等操作, 这些操作称为对集合的中间操作,最后可以对Stream进行关闭操作称为终端操作, 比如我们使用java.util.stream.Stream接口中的map方法:

1
2
3
fruitList.stream().map(item->{
return "&"+item+"&";
}).collect(Collectors.toList()).forEach(System.out::println);

这段代码的意思是将迭代(遍历)集合中的每一个元素,迭代过程中每遇到一个元素会将其传递给Lambda表达式对应的方法,由方法处理完成之后返回,此处是将每个元素的前后添加一个&符号,最后处理完所有元素后再将其转换为List集合 (collect 终端操作), 那么对于猜数字游戏中的如下代码意思是通过Stream接口的map方法,过程是将每一个元素转换成Tile对象,然后通过forEach迭代每一个元素并调用它的hide方法

1
2
tilePane.getChildren().stream().map(n->(Tile)n)
.forEach(Tile::hide);

初学者如果不理解Stream,只能通过大量的练习去熟练,如果能有函数式编程语言基础便更容易理解