侧边栏壁纸
  • 累计撰写 781 篇文章
  • 累计创建 1 个标签
  • 累计收到 1 条评论
标签搜索

stream

Dettan
2021-04-10 / 0 评论 / 0 点赞 / 108 阅读 / 27,501 字
温馨提示:
本文最后更新于 2022-04-30,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。
return Arrays.toString(states.stream().map(State::getIndex).toArray(Integer[]::new));

五、Lambda
Optionl.of( target ) .getOrElse( new ArrayList() ) .filter( Object::NotNull) .forEach( () -> {} )

这种写法可以确保绝大部分的异常不出现,特别是在对于集合进行处理的时候,因为集合中只要有其中一个值是会导致程序失败的,整个程序都会报错。这样写因为对数据做了比较多的检查和兼容,所以出现错误的概率会比较低,但也会有一个弊端,就是当这样的程序都出现异常的时候,开发者一般不知从何查起,要定位出是哪行数据就已经很费劲了。

集合的流式操作。
特点
不存储数据
No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
不修改原值,只计算结果(调用对象方法还是跟真的调用一样会改变source)
Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
惰性查找
Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
无限大小
Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
像iterator样只能用一次。
Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.


创建
From a Collection via the stream() and parallelStream() methods;
From an array via Arrays.stream(Object[]);
From static factory methods on the stream classes, such as Stream.of(Object[])IntStream.range(int, int) or Stream.iterate(Object, UnaryOperator);
The lines of a file can be obtained from BufferedReader.lines();
Streams of file paths can be obtained from methods in Files;
Streams of random numbers can be obtained from Random.ints();
Numerous other stream-bearing methods in the JDK, including BitSet.stream()Pattern.splitAsStream(java.lang.CharSequence), and JarFile.stream().

stream做过操作后就不能用了,只能用操作返回的stream,如果不返回stream,说明操作到头了,就要再创建一个steam。

map 和 foreach 区别:map对元素执行操作之后返回的还是stream,它是一个中间操作。而foreach是终止操作,返回void。

peek() , 把操作弄成单个的操作。对单个操作执行stream操作
peek 是执行操作,不用返回,直接影响原值
Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 2)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(Collectors.toList());
//结果
//        Filtered value: one
//        Mapped value: ONE
//        Filtered value: two
//        Mapped value: TWO
//        Filtered value: three
//        Mapped value: THREE
//        Filtered value: four
//        Mapped value: FOUR

limit(n) 只要前n个
skip(n) 跳过前n个
skip(n).limit(m) 从n开始的m个。
map 和 flatMap
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将映函数执行结果射成新的元素。 flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
String[] words = {"Hello", "World"};
Arrays.stream(words)     //imported
        .map(word -> word.split(""))
        .forEach(((String[] ss) -> System.out.println(Arrays.toString(ss)))); //结果是[H, e, l, l, o] , [W, o, r, l, d]
System.out.println("-----");

Arrays.stream(words)
.flatMap(s -> Stream.of(s.split("")))
.forEach(System.out::println); //结果是 [H, e, l, l, o , W, o, r, l, d]




groupBy

//The following will classify Person objects by city:
Map<Integer, List<Person>> peopleByCity = list2.stream().collect(groupingBy(Person::getAge));
peopleByCity.forEach((k, v) -> System.out.println(k + "  " + v));

//The following will classify Person objects by state and city, cascading two Collectors together:
list2
.stream()
.collect(groupingBy(Person::getAge,mapping(Person::getName, toSet())))
.forEach((k, v) -> System.out.println(k + " " + v));

Map<String, Map<Integer, List<Person>>> peopleByStateAndCity
= list2.stream().collect(groupingBy(Person::getName,groupingBy(Person::getAge)));




max()
返回的是本身,不是比较的那个值
list2.stream().max(Comparator.comparingInt(Person::getAge)).get().getAge();
reduce()
约归操作, 每次操作由两个操作数,一个是上次操作结果,一个是这次的值
boolean foundAny = false;
T result = null;
for (T element : this stream) {
   if (!foundAny) {
       foundAny = true;
       result = element;
   }
   else
       result = accumulator.apply(result, element);
}
return foundAny ? Optional.of(result) : Optional.empty();
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6, 0, 23);
System.out.println( nums.stream().reduce(Integer::sum));
System.out.println( nums.stream().reduce((a,b)->a*b));
System.out.println( nums.stream().reduce(Integer::max));
System.out.println("年龄最大的人");
System.out.println(list2.stream().reduce((left, now) -> left.getAge() > now.getAge() ? left : now));


toMap()
Map<String, Person> collect = list2.stream().collect(toMap(Person::getName, p -> p));



先贴上几个案例,水平高超的同学可以挑战一下:
1.
从员工集合中筛选出salary大于8000的员工,并放置到新的集合里。
2.
统计员工的最高薪资、平均薪资、薪资之和。
3.
将员工按薪资从高到低排序,同样薪资者年龄小者在前。
4.
将员工按性别分类,将员工按性别和地区分类,将员工按薪资是否高于8000分为两部分。
用传统的迭代处理也不是很难,但代码就显得冗余了,跟Stream相比高下立判。
1 Stream概述
Java 8 是一个非常成功的版本,这个版本新增的Stream,配合同版本出现的 Lambda ,给我们操作集合(Collection)提供了极大的便利。
那么什么是Stream
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream可以由数组或集合创建,对流的操作分为两种:
1.
中间操作,每次返回一个新的流,可以有多个。
2.
终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
另外,Stream有几个特性:
1.
stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
2.
stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
3.
stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
2 Stream的创建
Stream可以通过集合数组创建。
1、通过 java.util.Collection.stream() 方法用集合创建流
List<String> list = Arrays.asList("a", "b", "c");
// 创建一个顺序流
Stream<String> stream = list.stream();
// 创建一个并行流
Stream<String> parallelStream = list.parallelStream();
12345
2、使用java.util.Arrays.stream(T[] array)方法用数组创建流
int[] array={1,3,5,6,8};
IntStream stream = Arrays.stream(array);
12
3、使用Stream的静态方法:of()、iterate()、generate()
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
stream2.forEach(System.out::println);

Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
stream3.forEach(System.out::println);
1234567



输出结果:
0 3 6 90.67961569092719940.19143142088542830.8116932592396652
streamparallelStream的简单区分:
stream
是顺序流,由主线程按顺序对流执行操作,而
parallelStream
是并行流,内部以多线程并行执行的方式对流进行操作,但前提是流中的数据处理没有顺序要求。例如筛选集合中的奇数,两者的处理不同之处:
如果流中的数据量足够大,并行流可以加快处速度。
除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:
Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();
1
3 Stream的使用
在使用stream之前,先理解一个概念:Optional 。
Optional类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。更详细说明请见:菜鸟教程Java 8 Optional类
接下来,大批代码向你袭来!我将用20个案例将Stream的使用整得明明白白,只要跟着敲一遍代码,就能很好地掌握。
案例使用的员工类
这是后面案例中使用的员工类:
List<Person> personList = new ArrayList<Person>();
personList.add(new Person("Tom", 8900, "male", "New York"));
personList.add(new Person("Jack", 7000, "male", "Washington"));
personList.add(new Person("Lily", 7800, "female", "Washington"));
personList.add(new Person("Anni", 8200, "female", "New York"));
personList.add(new Person("Owen", 9500, "male", "New York"));
personList.add(new Person("Alisa", 7900, "female", "New York"));

class Person {
private String name; // 姓名private int salary; // 薪资private int age; // 年龄private String sex; //性别private String area; // 地区// 构造方法public Person(String name, int salary, int age,String sex,String area)

// 省略了get和set,请自行添加}
1234567891011121314151617181920212223242526




3.1 遍历/匹配(foreach/find/match)

Stream
也是支持类似集合的遍历和匹配元素的,只是
Stream
中的元素是以
Optional
类型存在的。
Stream
的遍历、匹配非常简单。
// import已省略,请自行添加,后面代码亦是public class StreamTest {
	public static void main(String[] args) {
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);
    // 遍历输出符合条件的元素
    list.stream().filter(x -&gt; x &gt; 6).forEach(System.out::println);
    // 匹配第一个
    Optional&lt;Integer&gt; findFirst = list.stream().filter(x -&gt; x &gt; 6).findFirst();
    // 匹配任意(适用于并行流)
    Optional&lt;Integer&gt; findAny = list.parallelStream().filter(x -&gt; x &gt; 6).findAny();
    // 是否包含符合特定条件的元素boolean anyMatch = list.stream().anyMatch(x -&gt; x &lt; 6);
    System.out.println(&quot;匹配第一个值:&quot; + findFirst.get());
    System.out.println(&quot;匹配任意一个值:&quot; + findAny.get());
    System.out.println(&quot;是否存在大于6的值:&quot; + anyMatch);
}

}
12345678910111213141516171819




3.2 筛选(filter)

筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
案例一:筛选出Integer集合中大于7的元素,并打印出来
public class StreamTest {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
		Stream<Integer> stream = list.stream();
		stream.filter(x -> x > 7).forEach(System.out::println);
	}
}
1234567
预期结果:
8 9
案例二: 筛选员工中工资高于8000的人,并形成新的集合。 形成新集合依赖collect(收集),后文有详细介绍。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
		personList.add(new Person("Anni", 8200, 24, "female", "New York"));
		personList.add(new Person("Owen", 9500, 25, "male", "New York"));
		personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
	List&lt;String&gt; fiterList = personList.stream().filter(x -&gt; x.getSalary() &gt; 8000).map(Person::getName)
			.collect(Collectors.toList());
	System.out.print(&quot;高于8000的员工姓名:&quot; + fiterList);
}

}
123456789101112131415



运行结果:
高于8000的员工姓名:[Tom, Anni, Owen]
3.3 聚合(max/min/count)
max
min
count
这些字眼你一定不陌生,没错,在mysql中我们常用它们进行数据统计。Java stream中也引入了这些概念和用法,极大地方便了我们对集合、数组的数据统计工作。
案例一:获取String集合中最长的元素。
public class StreamTest {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
	Optional&lt;String&gt; max = list.stream().max(Comparator.comparing(String::length));
	System.out.println(&quot;最长的字符串:&quot; + max.get());
}

}
12345678



输出结果:
最长的字符串:weoujgsd
案例二:获取Integer集合中的最大值。
public class StreamTest {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(7, 6, 9, 4, 11, 6);
	// 自然排序
	Optional&lt;Integer&gt; max = list.stream().max(Integer::compareTo);
	// 自定义排序
	Optional&lt;Integer&gt; max2 = list.stream().max(new Comparator&lt;Integer&gt;() {
		@Override
		public int compare(Integer o1, Integer o2) {
			return o1.compareTo(o2);
		}
	});
	System.out.println(&quot;自然排序的最大值:&quot; + max.get());
	System.out.println(&quot;自定义排序的最大值:&quot; + max2.get());
}

}
1234567891011121314151617



输出结果:
自然排序的最大值:11自定义排序的最大值:11
案例三:获取员工工资最高的人。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
		personList.add(new Person("Anni", 8200, 24, "female", "New York"));
		personList.add(new Person("Owen", 9500, 25, "male", "New York"));
		personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
	Optional&lt;Person&gt; max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
	System.out.println(&quot;员工工资最大值:&quot; + max.get().getSalary());
}

}
1234567891011121314



输出结果:
员工工资最大值:9500
案例四:计算Integer集合中大于6的元素的个数。
import java.util.Arrays;
import java.util.List;

public class StreamTest {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(7, 6, 4, 8, 2, 11, 9);

	long count = list.stream().filter(x -&gt; x &gt; 6).count();
	System.out.println(&quot;list中大于6的元素个数:&quot; + count);
}

}
1234567891011



输出结果:
list中大于6的元素个数:4
3.4 映射(map/flatMap)
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为mapflatMap
map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
案例一:英文字符串数组的元素全部改为大写。整数数组每个元素+3。
public class StreamTest {
	public static void main(String[] args) {
		String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
		List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());
	List&lt;Integer&gt; intList = Arrays.asList(1, 3, 5, 7, 9, 11);
	List&lt;Integer&gt; intListNew = intList.stream().map(x -&gt; x + 3).collect(Collectors.toList());

	System.out.println(&quot;每个元素大写:&quot; + strList);
	System.out.println(&quot;每个元素+3:&quot; + intListNew);
}

}
123456789101112



输出结果:
每个元素大写:[ABCD, BCDD, DEFDE, FTR]每个元素+3:[4, 6, 8, 10, 12, 14]
案例二:将员工的薪资全部增加1000。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
		personList.add(new Person("Anni", 8200, 24, "female", "New York"));
		personList.add(new Person("Owen", 9500, 25, "male", "New York"));
		personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
	// 不改变原来员工集合的方式
	List&lt;Person&gt; personListNew = personList.stream().map(person -&gt; {
		Person personNew = new Person(person.getName(), 0, 0, null, null);
		personNew.setSalary(person.getSalary() + 10000);
		return personNew;
	}).collect(Collectors.toList());
	System.out.println(&quot;一次改动前:&quot; + personList.get(0).getName() + &quot;--&gt;&quot; + personList.get(0).getSalary());
	System.out.println(&quot;一次改动后:&quot; + personListNew.get(0).getName() + &quot;--&gt;&quot; + personListNew.get(0).getSalary());

	// 改变原来员工集合的方式
	List&lt;Person&gt; personListNew2 = personList.stream().map(person -&gt; {
		person.setSalary(person.getSalary() + 10000);
		return person;
	}).collect(Collectors.toList());
	System.out.println(&quot;二次改动前:&quot; + personList.get(0).getName() + &quot;--&gt;&quot; + personListNew.get(0).getSalary());
	System.out.println(&quot;二次改动后:&quot; + personListNew2.get(0).getName() + &quot;--&gt;&quot; + personListNew.get(0).getSalary());
}

}
12345678910111213141516171819202122232425262728



输出结果:
一次改动前:Tom–>8900一次改动后:Tom–>18900二次改动前:Tom–>18900二次改动后:Tom–>18900
案例三:将两个字符数组合并成一个新的字符数组。
public class StreamTest {
	public static void main(String[] args) {
		List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
		List<String> listNew = list.stream().flatMap(s -> {
			// 将每个元素转换成一个stream
			String[] split = s.split(",");
			Stream<String> s2 = Arrays.stream(split);
			return s2;
		}).collect(Collectors.toList());
	System.out.println(&quot;处理前的集合:&quot; + list);
	System.out.println(&quot;处理后的集合:&quot; + listNew);
}

}
1234567891011121314



输出结果:
处理前的集合:[m-k-l-a, 1-3-5]处理后的集合:[m, k, l, a, 1, 3, 5]
3.5 归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
案例一:求Integer集合的元素之和、乘积和最大值。
public class StreamTest {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
		// 求和方式1
		Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
		// 求和方式2
		Optional<Integer> sum2 = list.stream().reduce(Integer::sum);
		// 求和方式3
		Integer sum3 = list.stream().reduce(0, Integer::sum);
	// 求乘积
	Optional&lt;Integer&gt; product = list.stream().reduce((x, y) -&gt; x * y);

	// 求最大值方式1
	Optional&lt;Integer&gt; max = list.stream().reduce((x, y) -&gt; x &gt; y ? x : y);
	// 求最大值写法2
	Integer max2 = list.stream().reduce(1, Integer::max);

	System.out.println(&quot;list求和:&quot; + sum.get() + &quot;,&quot; + sum2.get() + &quot;,&quot; + sum3);
	System.out.println(&quot;list求积:&quot; + product.get());
	System.out.println(&quot;list求和:&quot; + max.get() + &quot;,&quot; + max2);
}

}
1234567891011121314151617181920212223



输出结果:
list求和:29,29,29list求积:2112list求和:11,11
案例二:求所有员工的工资之和和最高工资。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
		personList.add(new Person("Anni", 8200, 24, "female", "New York"));
		personList.add(new Person("Owen", 9500, 25, "male", "New York"));
		personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
	// 求工资之和方式1:
	Optional&lt;Integer&gt; sumSalary = personList.stream().map(Person::getSalary).reduce(Integer::sum);
	// 求工资之和方式2:
	Integer sumSalary2 = personList.stream().reduce(0, (sum, p) -&gt; sum += p.getSalary(),
			(sum1, sum2) -&gt; sum1 + sum2);
	// 求工资之和方式3:
	Integer sumSalary3 = personList.stream().reduce(0, (sum, p) -&gt; sum += p.getSalary(), Integer::sum);

	// 求最高工资方式1:
	Integer maxSalary = personList.stream().reduce(0, (max, p) -&gt; max &gt; p.getSalary() ? max : p.getSalary(),
			Integer::max);
	// 求最高工资方式2:
	Integer maxSalary2 = personList.stream().reduce(0, (max, p) -&gt; max &gt; p.getSalary() ? max : p.getSalary(),
			(max1, max2) -&gt; max1 &gt; max2 ? max1 : max2);

	System.out.println(&quot;工资之和:&quot; + sumSalary.get() + &quot;,&quot; + sumSalary2 + &quot;,&quot; + sumSalary3);
	System.out.println(&quot;最高工资:&quot; + maxSalary + &quot;,&quot; + maxSalary2);
}

}
1234567891011121314151617181920212223242526272829



输出结果:
工资之和:49300,49300,49300最高工资:9500,9500
3.6 收集(collect)
collect,收集,可以说是内容最繁多、功能最丰富的部分了。从字面上去理解,就是把一个流收集起来,最终可以是收集成一个值也可以收集成一个新的集合。
collect主要依赖java.util.stream.Collectors类内置的静态方法。
3.6.1 归集(toList/toSet/toMap)
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toListtoSettoMap比较常用,另外还有toCollectiontoConcurrentMap等复杂一些的用法。
下面用一个案例演示toListtoSettoMap
public class StreamTest {
	public static void main(String[] args) {
		List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
		List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
		Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
	List&lt;Person&gt; personList = new ArrayList&lt;Person&gt;();
	personList.add(new Person(&quot;Tom&quot;, 8900, 23, &quot;male&quot;, &quot;New York&quot;));
	personList.add(new Person(&quot;Jack&quot;, 7000, 25, &quot;male&quot;, &quot;Washington&quot;));
	personList.add(new Person(&quot;Lily&quot;, 7800, 21, &quot;female&quot;, &quot;Washington&quot;));
	personList.add(new Person(&quot;Anni&quot;, 8200, 24, &quot;female&quot;, &quot;New York&quot;));
	
	Map&lt;?, Person&gt; map = personList.stream().filter(p -&gt; p.getSalary() &gt; 8000)
			.collect(Collectors.toMap(Person::getName, p -&gt; p));
	System.out.println(&quot;toList:&quot; + listNew);
	System.out.println(&quot;toSet:&quot; + set);
	System.out.println(&quot;toMap:&quot; + map);
}

}
12345678910111213141516171819



运行结果:
toList:[6, 4, 6, 6, 20]toSet:[4, 20, 6]toMap:{Tom=mutest.Person@5fd0d5ae, Anni=mutest.Person@2d98a335}
3.6.2 统计(count/averaging)
Collectors提供了一系列用于数据统计的静态方法:
计数:count
平均值:averagingIntaveragingLongaveragingDouble
最值:maxByminBy
求和:summingIntsummingLongsummingDouble
统计以上所有:summarizingIntsummarizingLongsummarizingDouble
案例:统计员工人数、平均工资、工资总额、最高工资。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
	// 求总数
	Long count = personList.stream().collect(Collectors.counting());
	// 求平均工资
	Double average = personList.stream().collect(Collectors.averagingDouble(Person::getSalary));
	// 求最高工资
	Optional&lt;Integer&gt; max = personList.stream().map(Person::getSalary).collect(Collectors.maxBy(Integer::compare));
	// 求工资之和
	Integer sum = personList.stream().collect(Collectors.summingInt(Person::getSalary));
	// 一次性统计所有信息
	DoubleSummaryStatistics collect = personList.stream().collect(Collectors.summarizingDouble(Person::getSalary));

	System.out.println(&quot;员工总数:&quot; + count);
	System.out.println(&quot;员工平均工资:&quot; + average);
	System.out.println(&quot;员工工资总和:&quot; + sum);
	System.out.println(&quot;员工工资所有统计:&quot; + collect);
}

}
123456789101112131415161718192021222324



运行结果:
员工总数:3员工平均工资:7900.0员工工资总和:23700员工工资所有统计:DoubleSummaryStatistics{count=3, sum=23700.000000,min=7000.000000, average=7900.000000, max=8900.000000}
3.6.3 分组(partitioningBy/groupingBy)
分区:将stream按条件分为两个Map,比如员工按薪资是否高于8000分为两部分。
分组:将集合分为多个Map,比如员工按性别分组。有单级分组和多级分组。
案例:将员工按薪资是否高于8000分为两部分;将员工按性别和地区分组
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, "male", "New York"));
		personList.add(new Person("Jack", 7000, "male", "Washington"));
		personList.add(new Person("Lily", 7800, "female", "Washington"));
		personList.add(new Person("Anni", 8200, "female", "New York"));
		personList.add(new Person("Owen", 9500, "male", "New York"));
		personList.add(new Person("Alisa", 7900, "female", "New York"));
	// 将员工按薪资是否高于8000分组
    Map&lt;Boolean, List&lt;Person&gt;&gt; part = personList.stream().collect(Collectors.partitioningBy(x -&gt; x.getSalary() &gt; 8000));
    // 将员工按性别分组
    Map&lt;String, List&lt;Person&gt;&gt; group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
    // 将员工先按性别分组,再按地区分组
    Map&lt;String, Map&lt;String, List&lt;Person&gt;&gt;&gt; group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
    System.out.println(&quot;员工按薪资是否大于8000分组情况:&quot; + part);
    System.out.println(&quot;员工按性别分组情况:&quot; + group);
    System.out.println(&quot;员工按性别、地区:&quot; + group2);
}

}
123456789101112131415161718192021



输出结果:
员工按薪资是否大于8000分组情况:{false=[mutest.Person@2d98a335, mutest.Person@16b98e56, mutest.Person@7ef20235], true=[mutest.Person@27d6c5e0, mutest.Person@4f3f5b24, mutest.Person@15aeb7ab]}
员工按性别分组情况:{female=[mutest.Person@16b98e56, mutest.Person@4f3f5b24, mutest.Person@7ef20235], male=[mutest.Person@27d6c5e0, mutest.Person@2d98a335, mutest.Person@15aeb7ab]}
员工按性别、地区:{female={New York=[mutest.Person@4f3f5b24, mutest.Person@7ef20235], Washington=[mutest.Person@16b98e56]}, male={New York=[mutest.Person@27d6c5e0, mutest.Person@15aeb7ab], Washington=[mutest.Person@2d98a335]}}
123
3.6.4 接合(joining)
joining可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
	String names = personList.stream().map(p -&gt; p.getName()).collect(Collectors.joining(&quot;,&quot;));
	System.out.println(&quot;所有员工的姓名:&quot; + names);
	List&lt;String&gt; list = Arrays.asList(&quot;A&quot;, &quot;B&quot;, &quot;C&quot;);
	String string = list.stream().collect(Collectors.joining(&quot;-&quot;));
	System.out.println(&quot;拼接后的字符串:&quot; + string);
}

}
1234567891011121314



运行结果:
所有员工的姓名:Tom,Jack,Lily拼接后的字符串:A-B-C
3.6.5 归约(reducing)
Collectors类提供的reducing方法,相比于stream本身的reduce方法,增加了对自定义归约的支持。
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
		personList.add(new Person("Tom", 8900, 23, "male", "New York"));
		personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
		personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
	// 每个员工减去起征点后的薪资之和(这个例子并不严谨,但一时没想到好的例子)
	Integer sum = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -&gt; (i + j - 5000)));
	System.out.println(&quot;员工扣税薪资总和:&quot; + sum);

	// stream的reduce
	Optional&lt;Integer&gt; sum2 = personList.stream().map(Person::getSalary).reduce(Integer::sum);
	System.out.println(&quot;员工薪资总和:&quot; + sum2.get());
}

}
12345678910111213141516



运行结果:
员工扣税薪资总和:8700员工薪资总和:23700
3.7 排序(sorted)
sorted,中间操作。有两种排序:
sorted():自然排序,流中元素需实现Comparable接口
sorted(Comparator com):Comparator排序器自定义排序
案例:将员工按工资由高到低(工资一样则按年龄由大到小)排序
public class StreamTest {
	public static void main(String[] args) {
		List<Person> personList = new ArrayList<Person>();
	personList.add(new Person(&quot;Sherry&quot;, 9000, 24, &quot;female&quot;, &quot;New York&quot;));
	personList.add(new Person(&quot;Tom&quot;, 8900, 22, &quot;male&quot;, &quot;Washington&quot;));
	personList.add(new Person(&quot;Jack&quot;, 9000, 25, &quot;male&quot;, &quot;Washington&quot;));
	personList.add(new Person(&quot;Lily&quot;, 8800, 26, &quot;male&quot;, &quot;New York&quot;));
	personList.add(new Person(&quot;Alisa&quot;, 9000, 26, &quot;female&quot;, &quot;New York&quot;));

	// 按工资升序排序(自然排序)
	List&lt;String&gt; newList = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName)
			.collect(Collectors.toList());
	// 按工资倒序排序
	List&lt;String&gt; newList2 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed())
			.map(Person::getName).collect(Collectors.toList());
	// 先按工资再按年龄升序排序
	List&lt;String&gt; newList3 = personList.stream()
			.sorted(Comparator.comparing(Person::getSalary).thenComparing(Person::getAge)).map(Person::getName)
			.collect(Collectors.toList());
	// 先按工资再按年龄自定义排序(降序)
	List&lt;String&gt; newList4 = personList.stream().sorted((p1, p2) -&gt; {
		if (p1.getSalary() == p2.getSalary()) {
			return p2.getAge() - p1.getAge();
		} else {
			return p2.getSalary() - p1.getSalary();
		}
	}).map(Person::getName).collect(Collectors.toList());

	System.out.println(&quot;按工资升序排序:&quot; + newList);
	System.out.println(&quot;按工资降序排序:&quot; + newList2);
	System.out.println(&quot;先按工资再按年龄升序排序:&quot; + newList3);
	System.out.println(&quot;先按工资再按年龄自定义降序排序:&quot; + newList4);
}

}
1234567891011121314151617181920212223242526272829303132333435



运行结果:
按工资自然排序:[Lily, Tom, Sherry, Jack, Alisa]按工资降序排序:[Sherry, Jack, Alisa,Tom, Lily]先按工资再按年龄自然排序:[Sherry, Jack, Alisa, Tom, Lily]先按工资再按年龄自定义降序排序:[Alisa, Jack, Sherry, Tom, Lily]
3.8 提取/组合
流也可以进行合并、去重、限制、跳过等操作。
public class StreamTest {
	public static void main(String[] args) {
		String[] arr1 = { "a", "b", "c", "d" };
		String[] arr2 = { "d", "e", "f", "g" };
	Stream&lt;String&gt; stream1 = Stream.of(arr1);
	Stream&lt;String&gt; stream2 = Stream.of(arr2);
	// concat:合并两个流 distinct:去重
	List&lt;String&gt; newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
	// limit:限制从流中获得前n个数据
	List&lt;Integer&gt; collect = Stream.iterate(1, x -&gt; x + 2).limit(10).collect(Collectors.toList());
	// skip:跳过前n个数据
	List&lt;Integer&gt; collect2 = Stream.iterate(1, x -&gt; x + 2).skip(1).limit(5).collect(Collectors.toList());

	System.out.println(&quot;流合并:&quot; + newList);
	System.out.println(&quot;limit:&quot; + collect);
	System.out.println(&quot;skip:&quot; + collect2);
}

}
12345678910111213141516171819



运行结果:
流合并:[a, b, c, d, e, f, g]limit:[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]skip:[3, 5, 7, 9, 11]
0

评论区