Stream是用函数式编程的方式对集合进行操作的工具。
流操作举例:
collect:
这个方法可以利用stream里的值生成某种集合的类型,比如List。
example:
List<String> collected = Stream.of("a","b","c")
.collect(Collectors.toList());
如果要获取的是Set集合,只要将toList改成toSet。这是将stream转换成集合,如果想将集合转换成stream呢?比如有一个List集合lists。很简单,只需要调用集合的stream方法:lists.stream(),这样就可以了。
map:
如果有一个函数可以将一种类型的值转换成另一种类型,那么map就可以使用这个函数,将一个流中的值转换成一个新的流。
example:
将放有小写字母的流用map转换成存放大写字母的流,然后再将流转换成集合。
List<String> collected = Stream.of("a","b","c")
.map(string -> string.toUpperCase())
.collect(Collectors.toList());
filter:
遍历并过滤流中的数据。
example:
统计字符串集合中包含”j”字符的元素的个数。
List<String> list = new ArrayList<>();
list.add("jack");
list.add("gbhtrbgr");
list.add("fertg");
list.add("tyjty");
Long val = list.stream()
.filter(x->x.contains("j"))
.count();
System.out.println(val);//2
flatMap:
如果有这样一个流,流中放有多个集合元素,在这个流后调用flatMap操作,可以将所有集合中的元素都放入一个新的流中作为元素,最后返回那个新的流。
example:
List<Integer> collected = Stream.of(Arrays.asList(3,5),Arrays.asList(7,1))
.flatMap(numbers->numbers.stream())
.collect(Collectors.toList());
max&min:
求最大值和最小值。
example:
找出人对象集合中姓名长度最大的人和长度最小的人。
List<Person> personList = Arrays.asList(new Person("Jack",18),new Person("Richard Warburton", 32),new Person("Amy", 45));
Person p1 = personList.stream()
.max(Comparator.comparing(Person::getLength))
.get();
System.out.println(p1);//Person [name=Richard Warburton, age=32]
Person p2 = personList.stream()
.min(Comparator.comparing(Person::getLength))
.get();
System.out.println(p2);//Person [name=Amy, age=45]
代码中的Person::getLength是调用类中的方法。
辅助类代码:
public class Person{
private String name;
private Integer age;
public Integer getLength(){//获取姓名字符串长度
return this.getName().length();
}
public Person(String name, Integer age) {
super();
this.name = name;
this.age = age;
}
public Person() {
super();
// TODO Auto-generated constructor stub
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
reduce:
reduce可以从一组值中生成一个值。
example:
求一组数的和。
int count = Stream.of(1,5,6,7)
.reduce(0, (acc,element)->acc+element);
System.out.println(count);//19
上面这段代码结果是19,表面上看结果是1+5+6+7的结果,但是reduce内部计算过程真的是这样的吗?假设是的话,那么把”acc+element“改成“acc-element”,那结果应该是1-5-6-7=-17,可真实的结果却是-19。其实,前面的推论忽视了reduce的第一个参数作用,第一个参数实际上是参与了运算的,真正的运算过程应该是0+1+5+6+7,所以做减法的话会得到0-1-5-6-7=-19。当然,实际reduce内部运算过程并不是这样一次性把数字都加起来最后返回结果,这里我只是试着用一种简单的思路去说明reduce第一个参数是参与运算的。
综合示例:
在《java8 函数式编程》这本书中还提供了一个比较贴合实际的综合例子:找出某张专辑中所有乐队的国籍。
案例分析:
专辑中的表演家除了乐队类型之外还有个人的艺术家,乐队名称假设”the”开头。要找出某张专辑中所有乐队的国籍。首先要从专辑中找出所有的表演者,再从表演者中找出所有的乐队,再找出每个乐队的国籍,最后把这些国籍用集合存放。
代码实现:
在原书中没有专辑等类的代码,可以自己实现,首先用一个Musician类代表乐队和个人的表演者,乐队和个人表演者都通过这个类去new出来,区别是乐队的name属性赋值时字符串“the”开头。这个类我只给了国籍属性,其他属性通过继承前面提到的Person类得到。
public class Musician extends Person {
private String nationality;
public Musician() {
super();
// TODO Auto-generated constructor stub
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
@Override
public String toString() {
return "Musician [nationality=" + nationality + "]";
}
}
然后是专辑类:
public class Album<T> {
private Stream<T> musicians;
private List<T> lists;
public Album() {
super();
lists = new ArrayList<>();
this.musicians = lists.stream();
}
public void addMusician(T t){
lists.add(t);
}
public Stream<T> getMusicians() {
return musicians;
}
public void setMusicians(Stream<T> musicians) {
this.musicians = musicians;
}
@Override
public String toString() {
return "Album [musicians=" + musicians + "]";
}
}
在专辑类中我为了不暴露类中的数据结构,对外返回的是stream,并提供一个addMusician方法去保护集合的添加方法,在书中也建议我们这样做。
最终的测试代码如下:
Musician m1 = new Musician();
m1.setName("stone");
m1.setNationality("America");
Musician m2 = new Musician();
m2.setName("sky");
m2.setNationality("China");
Musician m3 = new Musician();
m3.setName("theFlag");
m3.setNationality("England");
Musician m4 = new Musician();
m4.setName("theMouse");
m4.setNationality("French");
Album<Musician> album = new Album<>();
album.addMusician(m4);
album.addMusician(m3);
album.addMusician(m2);
album.addMusician(m1);
Set<String> nationalities = album.getMusicians()
.filter(m->m.getName().startsWith("the"))
.map(m->m.getNationality())
.collect(Collectors.toSet());
System.out.println(nationalities);//[French, England]
参考资料:
1、《java8 函数式编程》(Richard Warburton)