java8集合操作——Stream

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)

    原文作者:BOOM
    原文地址: https://zhuanlan.zhihu.com/p/32152541
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞