外部迭代到内部迭代
1.首先准备POJO类,Artist
public class Artist {
private String name;
private String from;
public Artist() {}
public Artist(String name, String from) {
this.name = name;
this.from = from;
}
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public String getFrom() { return from; }
public void setFrom(String from) { this.from = from; }
}
2.外部迭代:Java8之前如果在处理集合时,普遍先进行迭代,然后处理返回的每一个操作。
//准备数据
List<Artist> artistList = Arrays.asList(new Artist[]{new Artist("LP", "USA"),
new Artist("Imagine Dragon", "USA"), new Artist("Muse", "British"),
new Artist("Sum41", "Canada"), new Artist("Simple Plan", "Canada")});
//java8之前统计USA的乐队,外部迭代
long count = 0;
for(Artist artist : artistList){
if(artist.getFrom().equals("USA")){
count++;
}
}
System.out.println(count);
//高级for循环内部实现机制
count = 0;
Iterator<Artist> iterator = artistList.iterator();
while(iterator.hasNext()){
Artist artist = iterator.next();
if(artist.getFrom().equals("USA")){
count++;
}
}
System.out.println(count);
3.内部迭代:调用stream()方法,返回内部迭代的相应接口:Stream。
Stream是用函数式编程方法在集合类上进行复杂操作的工具。
//内部迭代,通过Stream:第一步过滤出所有美国乐队,第二部统计人数
count = artistList.stream().filter(artist -> artist.getFrom().equals("USA")).count();
System.out.println(count);
4.内部迭代实现机制
a.外部迭代只有一个for循环,内部迭代分两步操作,过滤和计数,是否使用两次循环,事实上,只需循环一次。
b.Stream接口有两种类型方法,一种是惰性求值,一种是及早求值
//惰性求值
artistList.stream().filter(artist -> {
System.out.println(artist.getName());
return artist.getFrom().equals("USA");
});
上面filter()方法是惰性求值,filter只描述Stream,最终并不产生新的集合,运行如上代码,不会输出艺术家的名字。
判断一个操作是惰性求值还是及早求值:只需判断它的返回值。如果返回值是Stream,惰性求值。如果返回值为空或其他,那么就是及早求值。
//惰性求值加及早求值
artistList.stream().filter(artist -> {
System.out.println(artist.getName());
return artist.getFrom().equals("USA");
}).count();
理想的使用方式就是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果。
5.产用的流操作
a.collect(toList())由Stream里的值产生一个列表,是及早求值的操作
//of,collect(toList())
List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList());
以上代码,首先由列表生成一个Stream,然后collect操作,由Stream生成列表。
b.map将一种类型的值转换为另外一种类型
//map(),其表达式必须为Function接口的一个实例
List<String> collected = Stream.of("a", "b", "hello").map(item -> item.toUpperCase()).collect(Collectors.toList());
c.filter遍历数据并检查其中的元素
List<String> beginWithNumbers = Stream.of("a", "1abc", "abc1").filter(value -> Character.isDigit(value.charAt(0))).collect(Collectors.toList());
d.flatMap可以用Stream替换值,然后将多个Stream连接成一个Stream
List<Integer> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4)).flatMap(numbers -> numbers.stream()).collect(Collectors.toList());
e.max和min Stream上常用的操作,求最大和最小值
List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 6, 7);
int min = numberList.stream().min(Comparator.comparing(number -> number)).get();
int max = numberList.stream().max(Comparator.comparing(number -> number)).get();
f.reduce操作
reduce操作可以对Stream()中的元素求和,0为起点,两个参数:传入Stream当前元素和acc,acc是累加器保存当前累加结果。
Stream.of(1, 2, 3).reduce(0, (acc, element) -> acc + element);
6.高阶函数
高阶函数是指接受另外一个函数作为参数,或返回一个函数的函数,辨认高阶函数:看函数签名,如果参数列表里包含函数接口,或者返回函数接口,就是高阶函数。
7.正确使用Lambda表达式
Lambda能够写出更简单代码,因为明确了要达成什么转化,而不是说明如何转化,这样代码潜在缺陷更少,更直接表达程序员用途。
另一层含义在于写出函数没有副作用。没有副作用的函数不会改变程序或者外界的状态。
//打印函数就是有副作用的
artistList.stream().filter(artist -> {
System.out.println(artist.getName());
return artist.getFrom().equals("USA");
}).count();
//赋值也是有副作用的,如下代码也不会编译通过
ActionEvent localEvent = null;
JButton jButton = new JButton().addActionListener(event ->{
localEvent = event;
});
使用Lambda获取值而不是变量,这样也会减少错误。