Java中Stream(流式编程)理解与使用
一、使用Stream的好处
Stream是Java 8提供的新功能,是对集合(Collection)对象功能的增强,能对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。 与Lambda 表达式结合,也可以提高编程效率、简洁性和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。通常编写并行代码很难而且容易出错, 但使用 Stream API 无需编写一行多线程的代码,就可以很方便地写出高性能的并发程序。
看下面的例子:我们先遍历姓“花”的到集合A,再找出名字是3个字的到集合B,最后遍历输出B中的元素。传统的做法是:
public class Demo1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("花和尚");
list.add("豹子头");
list.add("摸着天");
list.add("晁盖");
list.add("鼓上骚");
// 对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
List<String> listA = new ArrayList<>();
for (String s : list) {
if (s.startsWith("花")) {
listA.add(s);
}
}
// 对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
List<String> listB = new ArrayList<>();
for (String s : listA) {
if (s.length() == 3) {
listB.add(s);
}
}
// 遍历listB集合
for (String s : listB) {
System.out.println(s);
}
}
}
上面我们进行了3次遍历,每操作一次就会遍历一次,为了完成最终目标我们循环遍历了太多次。如果采用Stream + Lambda可以更简洁:
public class Demo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("花和尚");
list.add("豹子头");
list.add("摸着天");
list.add("晁盖");
list.add("鼓上骚");
list.stream().filter(name -> name.startsWith("花"))
.filter(name -> name.length() == 3)
.forEach(System.out::println);
}
}
二、Stream是什么?
Stream流其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何 元素(或其地址值),Stream是一个来自数据源的元素队列。它是有关算法和计算的,它更像一个高级版本的 Iterator。原始版本的 Iterator,用户只能显式地一个一个遍历元素并对其执行某些操作;高级版本的 Stream,用户只要给出需要对其包含的元素执行什么操作,比如 “过滤掉长度大于 10 的字符串”、“获取每个字符串的首字母”等,Stream 会隐式地在内部进行遍历,做出相应的数据转换。
需要注意的是:Stream 就如同一个迭代器(Iterator),单向,不可往复,数据只能遍历一次,遍历过一次后即用尽了,就好比流水从面前流过,一去不复返。
三、Stream流的构造 及 转换
Stream流的构造如下:
String [] stringArray = new String[]{"a", "b", "c"};
// 1. Stream.of()
Stream stream1 = Stream.of(stringArray);
// 2. Arrays.stream()
Stream stream2 = Arrays.stream(stringArray);
// 3. Collections.stream()
List<String> list = Arrays.asList(stringArray);
Stream stream3 = list.stream();
// 4. IntStream.range()
IntStream.range(1, 3).forEach(System.out::println);
// 5. IntStream.rangeClosed()
IntStream.rangeClosed(1, 3).forEach(System.out::println);
System.out.println("1>> " + stream1.equals(stream2));
System.out.println("2>> " + stream3.equals(stream2));
System.out.println("3>> " + stream3.equals(stream1));
System.out.println("4>> " + (stream3 == stream1));
System.out.println("5>> " + (stream3 == stream2));
System.out.println("6>> " + (stream2 == stream1));
range和rangeClosed就是[1,3)和[1,3]的区别。
1
2
1
2
3
1>> false
2>> false
3>> false
4>> false
5>> false
6>> false
Stream流的转换如下:
public class Demo4 {
public static void main(String[] args) {
String [] stringArray = new String[]{"a", "b", "c"};
// 1. stream.toArray
Stream stream1 = Stream.of(stringArray);
String [] strArray = (String[]) stream1.toArray(String []::new);
// 2. stream.collect()
List<String> list = Arrays.asList(stringArray);
Stream stream3 = list.stream();
List<String> list1 = (List<String>) stream3.collect(Collectors.toList());
System.out.println("1>> " + stringArray.equals(strArray));
System.out.println("2>> " + list.equals(list1));
System.out.println("4>> " + (stringArray == strArray));
System.out.println("5>> " + (list == list1));
}
}
四、Stream的操作类型
Intermediate:一个流可以后面跟随零个或多个 intermediate 操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
包含的操作有:map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered
Terminal:一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
包含的操作有:forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
下面对每一个常见的流操作进行举例介绍。
五、map/flatMap 操作
它们的作用就是把 input Stream 的每一个元素,映射成 output Stream 的另外一个元素。
public class Demo5 {
public static void main(String[] args) {
String [] stringArray = new String[]{"a", "b", "c"};
String [] newArray = (String[]) Arrays.stream(stringArray).map(String::toUpperCase).toArray(String[]::new);
Arrays.stream(newArray).forEach(System.out::println);
}
}
A
B
C
再比如:
public class Demo6 {
public static void main(String[] args) {
List<Integer> nums = Arrays.asList(1, 2, 3, 4);
List<Integer> squareNums = nums.stream().
map(n -> n * n).
collect(Collectors.toList());
squareNums.stream().forEach(System.out::println);
}
}
1
4
9
16
从上面例子可以看出,map 生成的是个 1:1 映射,每个输入元素,都按照规则转换成为另外一个元素。还有一些场景,是一对多映射关系的,这时需要 flatMap:
public class Demo7 {
public static void main(String[] args) {
Stream<List<Integer>> inputStream = Stream.of(
Arrays.asList(1),
Arrays.asList(1, 3),
Arrays.asList(4, 5, 6)
);
Stream<Integer> outputStream = inputStream.
flatMap((childList) -> childList.stream());
outputStream.forEach(System.out::println);
}
}
1
1
3
4
5
6
可以看出flatMap是把多个流集合在一起。
六、filter 操作
filter 对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream,就像我们的第一个例子。下面是提取出偶数:
public class Demo8 {
public static void main(String[] args) {
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens = Stream.of(sixNums).filter(n -> n%2 == 0).toArray(Integer[]::new);
}
}
七、forEach 操作
forEach 往往接收一个 Lambda 表达式,然后在 Stream 的每一个元素上执行该表达式,可以替代for循环。例子参考前面的代码。
八、peek 操作
peek 对每个元素执行操作并返回一个新的 Stream,可以在执行Terminal类型操作之前使用。比如上面的获取偶数的例子,可以在toArray()前进行打印操作:
public class Demo8 {
public static void main(String[] args) {
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Integer[] evens = Stream.of(sixNums).filter(n -> n%2 == 0).peek(System.out::println).toArray(Integer[]::new);
}
}
2
4
6
九、findFirst 操作
返回 Stream 的第一个元素,或者空。
public class Demo8 {
public static void main(String[] args) {
Integer[] sixNums = {1, 2, 3, 4, 5, 6};
Optional<Integer> first = Stream.of(sixNums).findFirst();
System.out.println(first);
System.out.println(first.get());
}
}
Optional[1]
1
findFirst 操作的返回值类型:Optional。这也是一个模仿 Scala 语言中的概念,作为一个容器,它可能含有某值,或者不包含。使用它的目的是尽可能避免 NullPointerException。
十、reduce 操作
主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相当于
Integer sum = integers.reduce(0, (a, b) -> a+b); 或
Integer sum = integers.reduce(0, Integer::sum);
也有没有起始值的情况,这时会把 Stream 的前面两个元素组合起来,返回的是 Optional。
public class Demo9 {
public static void main(String[] args) {
// 字符串连接,concat = "ABCD"
String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
System.out.println("concat :" + concat);
// 求最小值,minValue = -3.0
double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
System.out.println("minValue :" + minValue);
// 求和,sumValue = 10, 有起始值
int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
System.out.println("sumValue :" + sumValue);
// 求和,sumValue = 10, 无起始值
sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println("sumValue :" + sumValue);
// 过滤,字符串连接,concat = "ace"
concat = Stream.of("a", "B", "c", "D", "e", "F")
.filter(x -> x.compareTo("Z") > 0)
.peek(System.out::println)
.reduce("", String::concat);
System.out.println("concat :" + concat);
}
}
concat :ABCD
minValue :-3.0
sumValue :10
sumValue :10
a
c
e
concat :ace
上面代码例如第一个示例的 reduce(),第一个参数(空白字符)即为起始值,第二个参数(String::concat)为 BinaryOperator。
十一、limit/skip 操作
limit 返回 Stream 的前面 n 个元素;skip 则是扔掉前 n 个元素。
public class Person {
public int no;
private String name;
public Person (int no, String name) {
this.no = no;
this.name = name;
}
public String getName() {
System.out.println(name);
return name;
}
}
public class Demo10 {
public static void main(String[] args) {
testLimitAndSkip();
}
public static void testLimitAndSkip() {
List<Person> persons = new ArrayList();
for (int i = 1; i <= 10000; i++) {
Person person = new Person(i, "name" + i);
persons.add(person);
}
List<String> personList2 = persons.stream().
map(Person::getName).limit(10).skip(3).collect(Collectors.toList());
System.out.println(personList2);
}
}
name1
name2
name3
name4
name5
name6
name7
name8
name9
name10
[name4, name5, name6, name7, name8, name9, name10]
十二、sorted 操作
对 Stream 的排序通过 sorted 进行,它比数组的排序更强之处在于你可以首先对 Stream 进行各类 map、filter、limit、skip 甚至 distinct 来减少元素数量后,再排序,这能帮助程序明显缩短执行时间。
public class Demo11 {
public static void main(String[] args) {
testLimitAndSkip();
}
public static void testLimitAndSkip() {
List<Person> persons = new ArrayList();
for (int i = 1; i <= 5; i++) {
Person person = new Person(i, "name" + i);
persons.add(person);
}
List<Person> personList2 = persons.stream()
.limit(2)
.sorted((p1, p2) -> p1.getName().compareTo(p2.getName()))
.peek(p -> System.out.println("====>" + p.getName()))
.collect(Collectors.toList());
}
}
name2
name1
name1
====>name1
name2
====>name2
或者
public class Demo12 {
public static void main(String[] args) {
testLimitAndSkip();
}
public static void testLimitAndSkip() {
List<Integer> nums = new ArrayList();
nums.add(10);
nums.add(1);
nums.add(6);
nums.add(9);
nums.add(3);
List<Integer> personList2 = nums.stream()
.sorted(Comparator.reverseOrder())
.peek(p -> System.out.println("====>" + p))
.collect(Collectors.toList());
}
}
====>10
====>9
====>6
====>3
====>1
这里需要注意排序依据:
下面代码以自然序排序一个list
list.stream().sorted()
自然序逆序元素,使用Comparator 提供的reverseOrder() 方法
list.stream().sorted(Comparator.reverseOrder())
使用Comparator 来排序一个list
list.stream().sorted(Comparator.comparing(Student::getAge))
把上面的元素逆序
list.stream().sorted(Comparator.comparing(Student::getAge).reversed())
十三、parallel操作
并行和并发有什么区别?
并发当有多个线程在操作时,如果系统只有一个CPU,则它根本不可能真正同时进行一个以上的线程,它只能把CPU运行时间划分成若干个时间段,再将时间 段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。这种方式我们称之为并发(Concurrent)。
并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行,这种方式我们称之为并行(Parallel)。
使用stream的parallel操作可以使得循环是并行的,而不是一个循环结束下一个才能开始,最终的总处理时间取决于时间最长的那次循环,可以大大提高效率。
补充一:
上面的是较简单的代码演示,复杂的情况(比如Map类型数据的比较,分组等)咋办?可以参考下面的方式》代码详情参见 : https://github.com/ImOk520/myspringcloud
/**
* Lambda 示例代码
* Feng, Ge 2020年01月16日14:42:43
*/
/**
* (1)Intermediate(中间操作):
* 一个流可以后面跟随零个或多个 intermediate 操作。
* 其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。
* 这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
* map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、
* limit、 skip、 parallel、 sequential、 unordered
* <p>
* (2)Terminal(终止操作):
* 一个流只能有一个 terminal 操作,当这个操作执行后,流就被使用“光”了,无法再被操作。
* 所以这必定是流的最后一个操作。Terminal 操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个 side effect。
* forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator
* <p>
* (3)short-circuiting(短路操作)。
* 用以指:
* • 对于一个 intermediate 操作,如果它接受的是一个无限流,它可以返回一个有限的新 Stream。
* • 对于一个 terminal 操作,如果它接受的是一个无限流,但能在有限的时间计算出结果。
* anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 limit
* <p>
* Feng, Ge 2020-10-20 17:28
*/
public class MyLambdaTest {
@Test
public void test1() {
List list = StreamAPI.getStringList();
Stream distinct = list.stream().distinct();
System.out.println("【1】:" + distinct);
Object collect = distinct.collect(Collectors.toList());
System.out.println("【2】:" + collect);
long count = list.stream().distinct().count();
System.out.println("【3】:" + count);
}
@Test
public void test2() {
List<Integer> intList = StreamAPI.getIntegerList();
Stream<Integer> stream1 = intList.stream().filter(element -> element > 5);
stream1.forEach(System.out::println);
System.out.println("==============================================================");
List<String> strList = StreamAPI.getStringList();
Stream<String> stream2 = strList.stream().filter(element -> Integer.valueOf(element).intValue() > 5);
stream2.forEach(System.out::println);
System.out.println("==============================================================");
List<String> list = StreamAPI.getStringList();
Stream<String> stream = list.stream().filter(element -> !element.contains("8"));
stream.forEach(System.out::println);
boolean hasDiagInfo = false;
if (!org.springframework.util.CollectionUtils.isEmpty(StreamAPI.getBNullList())) {
List<B> diagnoseInfos = StreamAPI.getBNullList().stream().filter(dia -> StringUtils.isNotEmpty(dia.getName())).collect(Collectors.toList());
if (diagnoseInfos != null && diagnoseInfos.size() > 0){
hasDiagInfo = true;
}
System.out.println(diagnoseInfos);
System.out.println(hasDiagInfo);
}
}
@Test
public void test3() {
List<String> strList = StreamAPI.getStringList();
Stream<Integer> stream = strList.stream().map(String::length);
stream.forEach(System.out::println);
System.out.println("==============================================================");
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
List<Integer> collect = numbers.stream().map(num -> num * 2).collect(Collectors.toList());
collect.stream().forEach(System.out::println);
System.out.println("==============================================================");
List<B> bList = StreamAPI.getBList();
List<String> nameList = bList.stream().map(b -> b.getName()).collect(Collectors.toList());
nameList.stream().forEach(System.out::println);
}
/**
* anyMatch(),只要有一个元素匹配传入的条件,就返回 true。
* allMatch(),只有有一个元素不匹配传入的条件,就返回 false;如果全部匹配,则返回 true。
* noneMatch(),只要有一个元素匹配传入的条件,就返回 false;如果全部匹配,则返回 true。
*/
@Test
public void test4() {
List<B> bList = StreamAPI.getBList();
boolean anyMatchFlag = bList.stream().anyMatch(element -> element.getName().contains("小"));
boolean allMatchFlag = bList.stream().allMatch(element -> element.getName().length() > 1);
boolean noneMatchFlag = bList.stream().noneMatch(element -> element.getName().startsWith("小"));
System.out.println(anyMatchFlag);
System.out.println(allMatchFlag);
System.out.println(noneMatchFlag);
}
/**
* Stream.reduce()合并流的元素并产生单个值。
*/
@Test
public void test5() {
int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int sum = 0;
for (int i : numbers) {
sum += i;
}
System.out.println("sum : " + sum); // 55
System.out.println("==============================================================");
int sum1 = Arrays.stream(numbers).reduce(0, (a, b) -> a + b);
System.out.println("sum : " + sum1); // 55
System.out.println("==============================================================");
int sum2 = Arrays.stream(numbers).reduce(0, Integer::sum);
System.out.println("sum : " + sum2); // 55
System.out.println("==============================================================");
int max = Arrays.stream(numbers).reduce(0, (a, b) -> a > b ? a : b); // 10
int max1 = Arrays.stream(numbers).reduce(0, Integer::max); // 10
System.out.println("max : " + max);
System.out.println("max1 : " + max1);
System.out.println("==============================================================");
int min = Arrays.stream(numbers).reduce(0, (a, b) -> a < b ? a : b); // 0
int min1 = Arrays.stream(numbers).reduce(0, Integer::min); // 0
System.out.println("min : " + min);
System.out.println("min1 : " + min1);
System.out.println("==============================================================");
String[] strings = {"a", "b", "c", "d", "e"};
// |a|b|c|d|e , the initial | join is not what we want
String reduce = Arrays.stream(strings).reduce("", (a, b) -> a + "|" + b);
System.out.println("reduce : " + reduce);
// a|b|c|d|e, filter the initial "" empty string
String reduce2 = Arrays.stream(strings).reduce("", (a, b) -> {
if (!"".equals(a)) {
return a + "|" + b;
} else {
return b;
}
});
System.out.println("reduce2 : " + reduce2);
// a|b|c|d|e , better uses the Java 8 String.join :) (最好使用 Java 8 的 String.join)
String join = String.join("|", strings);
System.out.println("join : " + join);
}
/**
* 根据limit(n),直接截断流,后续操作不继续,限制其流中元素个数为n,此操作称为短路操作,提高效率时可以考虑短路操作
*/
@Test
public void test6() {
List<B> bList = StreamAPI.getBList();
bList.stream().filter(b -> b.getAge() > 3).limit(2).forEach(System.out::println);
}
/**
* averagingDouble-求平均值并转换成Double类型,同理还有averagingInt、averagingLong
*/
@Test
public void test7() {
List<B> bList = StreamAPI.getBList();
Double ageAve = bList.stream().collect(Collectors.averagingDouble(B::getAge));
System.out.println(ageAve);
}
/**
* summingDouble-求和转换成Double类型,同理还有summingInt、summingLong
*/
@Test
public void test8() {
List<B> bList = StreamAPI.getBList();
Double ageAve = bList.stream().collect(Collectors.summingDouble(B::getAge));
System.out.println(ageAve);
}
/**
* maxBy-根据函数条件求最大值 ,minBy-根据函数条件求最小值
*/
@Test
public void test9() {
List<B> bList = StreamAPI.getBList();
Optional<B> stuOptional = bList.stream().collect(Collectors.maxBy((a, b) -> Double.compare(a.getAge(), b.getAge())));
System.out.println(stuOptional.get());
}
/**
* groupingBy-分组,支持多级分组
*/
@Test
public void test10() {
List<B> bList = StreamAPI.getBList();
Map<Integer, List<B>> ageGroup = bList.stream().collect(Collectors.groupingBy(B::getAge));
Object single = JSONObject.toJSON(ageGroup);
System.out.println(ageGroup);
System.out.println(single);
System.out.println("===================多级分组(先姓名后年龄)====================");
Map<String, Map<String, List<B>>> groupmap = bList.stream()
.collect(Collectors.groupingBy(B::getName, Collectors.groupingBy((e) -> {
if (e.getAge() <= 20) {
return "年轻人";
} else if (e.getAge() <= 50) {
return "中年人";
} else {
return "老年人";
}
})));
System.out.println(groupmap);
Object mul = JSONObject.toJSON(groupmap);
System.out.println(mul);
System.out.println("===================多级分组(先姓名后年龄)====================");
Map<Integer, Map<String, List<B>>> groupmap1 = bList.stream()
.collect(Collectors.groupingBy(B::getAge, Collectors.groupingBy(B::getName)));
System.out.println(groupmap1);
Object mul1 = JSONObject.toJSON(groupmap1);
System.out.println(mul1);
System.out.println("===================Map分组====================");
List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "ok");
map1.put("age", 6);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "okok");
map2.put("age", 66);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "okokok");
map3.put("age", 666);
Map<String, Object> map4 = new HashMap<>();
map4.put("name", "ok");
map4.put("age", 16);
resultList.add(map1);
resultList.add(map2);
resultList.add(map3);
resultList.add(map4);
Map<String, List<Map<String, Object>>> result = resultList.stream().collect(Collectors.groupingBy(MyLambdaTest::getGroupKey));
System.out.println(JSONUtils.toJSONString(result));
}
private static String getGroupKey (Map<String, Object> map) {
return map.get("name").toString();
}
/**
* partitioningBy-分区
*/
@Test
public void test11() {
List<B> bList = StreamAPI.getBList();
Map<Boolean, List<B>> booleanGroup = bList.stream()
.collect(Collectors.partitioningBy((e) -> e.getAge() > 9));
System.out.println(booleanGroup);
}
/**
* joining-连接字符串
*/
@Test
public void test12() {
List<B> bList = StreamAPI.getBList();
String nameStr = bList.stream().map(B::getName)
.collect(Collectors.joining());
System.out.println(nameStr);
System.out.println("=======================================");
String nameSplitStr = bList.stream().map(B::getName)
.collect(Collectors.joining(","));
System.out.println(nameSplitStr);
}
/**
* sorted-排序,和reversed结合使用实现正序、倒序
*/
@Test
public void test13() {
List<B> bList = StreamAPI.getBList();
bList.stream()
.sorted(Comparator.comparing(B::getAge).reversed()).forEach(e -> {
System.out.println(e);
});
System.out.println("=================Map比较排序======================");
List<Map<String, Object>> resultList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "ok");
map1.put("age", 6);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "okok");
map2.put("age", 66);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "okokok");
map3.put("age", 666);
resultList.add(map1);
resultList.add(map2);
resultList.add(map3);
Stream<Map<String, Object>> sorted = resultList.stream().sorted(Comparator.comparing(MyLambdaTest::comparedByAge).reversed());
List<Map<String, Object>> collect = sorted.collect(Collectors.toList());
System.out.println(JSONUtils.toJSONString(collect));
}
private static long comparedByAge(Map<String, Object> map) {
return Long.parseLong(map.get("age").toString());
}
/**
* Stream流中的常用方法skip:用于跳过元素
* 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流
* 如果流的当前长度大于n,则跳过前n个,否则会得到一个长度为0的空流
*/
@Test
public void test14() {
List<String> strings = StreamAPI.getStringList();
Stream stream = strings.stream();
Stream streamLimit = stream.skip(2);
streamLimit.forEach(s -> System.out.println(s));
}
/**
* parallelStream() <===> stream().parallel()
* parallelStream提供了流的并行处理,它是Stream的另一重要特性,其底层使用Fork/Join框架实现。
* 简单理解就是多线程异步任务的一种实现。
* 我们可以通过虚拟机启动参数 -Djava.util.concurrent.ForkJoinPool.common.parallelism=N 来设置worker的数量。
* <p>
* 并行可能带来的问题:
* 由于并行流使用多线程,则一切线程安全问题都应该是需要考虑的问题,如:资源竞争、死锁、事务、可见性等等。
* 如果某个生产者生产了许多重量级的任务(耗时很长),那么其他任务毫无疑问将会没有工作线程可用;更可怕的事情是这些工作线程正在进行IO阻塞。
* 串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
* 并行流:适合没有线程安全问题、较单纯的数据处理任务。
*/
@Test
public void test15() {
List<B> bList = StreamAPI.getBList();
bList.parallelStream().forEach(num ->
System.out.println(Thread.currentThread().getName() + ">>" + num.getName()));
System.out.println("=======================================");
bList.stream().parallel().forEach(num ->
System.out.println(Thread.currentThread().getName() + ">>" + num.getName()));
}
/**
* 加上.sequential(), 将并行流变成顺序流(串行流)
* 串行流:适合存在线程安全问题、阻塞任务、重量级任务,以及需要使用同一事务的逻辑。
*/
@Test
public void test16() {
List<B> bList = StreamAPI.getBList();
bList.parallelStream().sequential().forEach(num ->
System.out.println(Thread.currentThread().getName() + ">>" + num.getName()));
}
/**
* forEach 和 forEachOrdered
* 因为是并行处理
* 第一种输出的情况不确定
* 第二行输出的一直是确定的
*/
@Test
public void test17() {
Stream.of("AAA", "BBB", "CCC").parallel().forEach(s -> System.out.println("Output:" + s));
System.out.println("=======================================");
Stream.of("AAA", "BBB", "CCC").parallel().forEachOrdered(s -> System.out.println("Output:" + s));
}
@Test
public void test18() {
int[] array = {1, 2, 5, 5, 5, 5, 6, 6, 7, 2, 9, 2};
List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
/*int[]转list*///java8及以上版本
List<Integer> list1 = Arrays.stream(array).boxed().collect(Collectors.toList());
/*list转int[]*/
//方法一:
Integer[] intArr = numbers.toArray(new Integer[numbers.size()]);
//方法二:java8及以上版本
int[] intArr1 = numbers.stream().mapToInt(Integer::valueOf).toArray();
}
}
补充二、for、foreach、stream 哪家的效率更高?
- 1万以内的数据,for循环的性能要高于foreach和stream;
- 数据量上去之后(1000万),三种遍历方式基本已经没有什么差距了,但是Stream提供并行处理,在数据量大了之后,效率会明显增强。(但是单核CPU,Stream并行处理可能会效率更慢)