Lambda表达式和stream

Lambda表达式

语法格式一:无参数,无返回值

()->System.out.println("Hello,Lambda")

语法格式二:有一个参数,并且无返回值

(x)->System.out.println(x)

语法格式三:若只有一个参数,小括号可以省略不写

x->System.out.print(x)

语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句

Comparator<Integer>com=(x,y)->{     System.out.print("函数式接口");     return Integer.compare(x,y);}

语法格式五:若Lamdba体中只有一条语句,return和大括号都可以省略不写

Comparator<Integer>com=(x,y)->Integer.compare(x,y);

语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“数据推断”

(Integer x,Integer y)->Integer.compare(x,y)

Stream API代替for循环

List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");
// 将字符串list转为管道流Stream
List<String> list = nameStrs.stream()
   // 使用filter过滤所有是L大写字母开头的字符串
      .filter(s -> s.startsWith("L"))
   // 然后过滤的字符串全部转为大写字母
      .map(String::toUpperCase)
   // 使用sorted方法进行排序
      .sorted()
   // 使用collect方法将管道流转为List
      .collect(toList());
System.out.println(list);
  • 首先,我们使用Stream()函数,将一个List转换为管道流

  • 调用filter函数过滤数组元素,过滤方法使用lambda表达式,以L开头的元素返回true被保留,其他的List元素被过滤掉

  • 然后调用Map函数对管道流中每个元素进行处理,字母全部转换为大写

  • 然后调用sort函数,对管道流中数据进行排序

  • 最后调用collect函数toList,将管道流转换为List返回

Limit与Skip管道数据截取

List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
      .limit(2)
      .collect(Collectors.toList());
List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
      .skip(2)
      .collect(Collectors.toList());
  • limt方法传入一个整数n,用于截取管道中的前n个元素。经过管道处理之后的数据是:[Monkey, Lion]。

  • skip方法与limit方法的使用相反,用于跳过前n个元素,截取从n到末尾的元素。经过管道处理之后的数据是: [Giraffe, Lemur]

Distinct元素去重

List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
      .distinct()
      .collect(Collectors.toList());

上面代码去重之后的结果是: ["Monkey", "Lion", "Giraffe", "Lemur"]

Sorted排序

默认的情况下,sorted是按照字母的自然顺序进行排序。如下代码的排序结果是:[Giraffe, Lemur, Lion, Monkey],字数按顺序G在L前面,L在M前面。第一位无法区分顺序,就比较第二位字母。

List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur")
      .sorted()
      .collect(Collectors.toList());

学习一下HashMap的merge()函数

  • 参数一:向map里面put的键

  • 参数二:向map里面put的值

  • 参数三:如果键发生重复,如何处理值。可以是一个函数,也可以写成lambda表达式。

        String k = "key";
       HashMap<String, Integer> map = new HashMap<String, Integer>() {{
           put(k, 1);
      }};
       map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);

看上面一段代码,我们首先创建了一个HashMap,并往里面放入了一个键值为k:1的元素。当我们调用merge函数,往map里面放入k:2键值对的时候,k键发生重复,就执行后面的lambda表达式。表达式的含义是:返回旧值oldVal加上新值newVal(1+2),现在map里面只有一项元素那就是k:3。

按Map的键排序

// 创建一个Map,并填入数据
Map<String, Integer> codes = new HashMap<>();
codes.put("United States", 1);
codes.put("Germany", 49);
codes.put("France", 33);
codes.put("China", 86);
codes.put("Pakistan", 92);

// 按照Map的键进行排序
Map<String, Integer> sortedMap = codes.entrySet().stream()    
      .sorted(Map.Entry.comparingByKey())
      .collect(
               Collectors.toMap(
                   Map.Entry::getKey,
                   Map.Entry::getValue,
                  (oldVal, newVal) -> oldVal,
                   LinkedHashMap::new
              )
      );

// 将排序后的Map打印
sortedMap.entrySet().forEach(System.out::println);

看上文中第二段代码:

  • 首先使用entrySet().stream() 将Map类型转换为Stream流类型。

  • 然后使用sorted方法排序,排序的依据是Map.Entry.comparingByKey(),也就是按照Map的键排序

  • 最后用collect方法将Stream流转成LinkedHashMap。 其他参数都好说,重点看第三个参数,就是一个merge规则的lambda表达式,与merge方法的第三个参数的用法一致。由于本例中没有重复的key,所以新值旧值随便返回一个即可。

上面的程序将在控制台上打印以下内容,键(国家/地区名称)以自然字母顺序排序:

China=86
France=33
Germany=49
Pakistan=92
United States=1
请注意使用LinkedHashMap来存储排序的结果以保持顺序。默认情况下,Collectors.toMap()返回HashMap。HashMap不能保证元素的顺序。

按Map的键排序

Map<String, Integer> sortedMap2 = codes.entrySet().stream()
      .sorted(Map.Entry.comparingByValue())
      .collect(Collectors.toMap(
               Map.Entry::getKey,
               Map.Entry::getValue,
              (oldVal, newVal) -> oldVal,
               LinkedHashMap::new));

sortedMap2.entrySet().forEach(System.out::println);

这是显示Map按值排序的输出:

United States=1
France=33
Germany=49
China=86
Pakistan=92

使用TreeMap按键排序

大家可能都知道TreeMap内的元素是有顺序的,所以利用TreeMap排序也是可取的一种方法。您需要做的就是创建一个TreeMap对象,并将数据从HashMapput到TreeMap中,非常简单:

// 将 `HashMap` 转为 `TreeMap`
Map<String, Integer> sorted = new TreeMap<>(codes);

输出:

China=86
France=33
Germany=49
Pakistan=92
United States=1

ForEach和ForEachOrdered

如果我们只是希望将Stream管道流的处理结果打印出来,而不是进行类型转换,我们就可以使用forEach()方法或forEachOrdered()方法。

Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
      .parallel()
      .forEach(System.out::println);
Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion")
      .parallel()
      .forEachOrdered(System.out::println);
  • parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证

  • forEachOrdered从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。也就是下面的样子(forEach方法则无法保证这个顺序):

Monkey
Lion
Giraffe
Lemur
Lion

元素的收集collect

java Stream 最常见的用法就是:一将集合类转换成管道流,二对管道流数据处理,三将管道流处理结果在转换成集合类。那么collect()方法就为我们提供了这样的功能:将管道流处理结果在转换成集合类。

1.收集为Set

通过Collectors.toSet()方法收集Stream的处理结果,将所有元素收集到Set集合中。

Set<String> collectToSet = Stream.of(
  "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.toSet());

//最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意Set会去重。

2.收集到List

同样,可以将元素收集到List使用toList()收集器中。

List<String> collectToList = Stream.of(
  "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toList());

// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

3.通用的收集方式

上面为大家介绍的元素收集方式,都是专用的。比如使用Collectors.toSet()收集为Set类型集合;使用Collectors.toList()收集为List类型集合。那么,有没有一种比较通用的数据元素收集方式,将数据收集为任意的Collection接口子类型。 所以,这里就像大家介绍一种通用的元素收集方式,你可以将数据元素收集到任意的Collection类型:即向所需Collection类型提供构造函数的方式。

LinkedList<String> collectToCollection = Stream.of(
  "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
).collect(Collectors.toCollection(LinkedList::new));

//最终collectToCollection中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

注意:代码中使用了LinkedList::new,实际是调用LinkedList的构造函数,将元素收集到Linked List。当然你还可以使用诸如LinkedHashSet::newPriorityQueue::new将数据元素收集为其他的集合类型,这样就比较通用了。

4.收集到Array

通过toArray(String[]::new)方法收集Stream的处理结果,将所有元素收集到字符串数组中。

String[] toArray = Stream.of(
  "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
) .toArray(String[]::new);

//最终toArray字符串数组中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]

5.收集到Map

使用Collectors.toMap()方法将数据元素收集到Map里面,但是出现一个问题:那就是管道中的元素是作为key,还是作为value。我们用到了一个Function.identity()方法,该方法很简单就是返回一个“ t -> t ”(输入就是输出的lambda表达式)。另外使用管道流处理函数distinct()来确保Map键值的唯一性。

Map<String, Integer> toMap = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.distinct()
.collect(Collectors.toMap(
      Function.identity(),   //元素输入就是输出,作为key
      s -> (int) s.chars().distinct().count()// 输入元素的不同的字母个数,作为value
));

// 最终toMap的结果是: {Monkey=6, Lion=4, Lemur=5, Giraffe=6}  

6.分组收集groupingBy

Collectors.groupingBy用来实现元素的分组收集,下面的代码演示如何根据首字母将不同的数据元素收集到不同的List,并封装为Map。

Map<Character, List<String>> groupingByList =  Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur", "Lion"
)
.collect(Collectors.groupingBy(
      s -> s.charAt(0) ,  //根据元素首字母分组,相同的在一组
      // counting()       // 加上这一行代码可以实现分组统计
));

// 最终groupingByList内的元素: {G=[Giraffe], L=[Lion, Lemur, Lion], M=[Monkey]}
//如果加上counting() ,结果是: {G=1, L=3, M=1}

#### 其他常用方法

boolean containsTwo = IntStream.of(1, 2, 3).anyMatch(i -> i == 2);
// 判断管道中是否包含2,结果是: true

long nrOfAnimals = Stream.of(
   "Monkey", "Lion", "Giraffe", "Lemur"
).count();
// 管道中元素数据总计结果nrOfAnimals: 4


int sum = IntStream.of(1, 2, 3).sum();
// 管道中元素数据累加结果sum: 6


OptionalDouble average = IntStream.of(1, 2, 3).average();
//管道中元素数据平均值average: OptionalDouble[2.0]



int max = IntStream.of(1, 2, 3).max().orElse(0);
//管道中元素数据最大值max: 3



IntSummaryStatistics statistics = IntStream.of(1, 2, 3).summaryStatistics();
// 全面的统计结果statistics: IntSummaryStatistics{count=3, sum=6, min=1, average=2.000000, max=3}

 

原文地址:https://www.cnblogs.com/liuli-warriors/p/12659529.html