好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

都9102年了,你还用for循环操作集合吗

前言

前段时间公司书架多了一本《 java 8 实战》,毕竟久闻lambda的大名,于是借来一阅。这一看,简直是惊为天人啊,lambda,stream,java8里简直是满脑子骚操作,看我的一愣一愣的。我甚至是第一次感觉到了什么叫优雅。

本文主要介绍java8中的流处理,看看java8是怎么愉快的玩耍 集合 的,让我们来一起感受java8的魅力吧!

我就随便举个例子,看看stream有多优雅。

?

1

2

3

4

5

6

7

// 对苹果按颜色汇总并绩数量

map<string, long > applecount = apples.stream()

  .collect(groupingby(apple::getcolor, counting()));

// 过滤掉颜色为黑色的苹果,并汇总好苹果的总金额

double sum = apples.stream()

  .filter(i-> "black" .equals(i.getcolor()))

  .collect(tolist);

一、lambda表达式

虽然本文重点是stream,但是stream中需要传递lambda表达式,所以简单介绍一下lambda表达式。lambda表达式其实就是匿名函数(anonymous function),是指一类无需定义标识符的函数或子程序。

java中匿名函数的表现形式,只留下入参和方法体中的内容

?

1

2

3

4

5

6

// 普通函数

public void run(string s){

  system.out.print(s+ "哈哈" );

}

// 我不要名字啦!!!

(s)->system.out.print(s+ "哈哈" )

诶,过去我们都用对象调方法的,你弄这个没名的东西啥时候用啊?

java中我们通过函数式接口来使用这种匿名函数。

函数式接口

1.java中只包含一个未实现方法的接口。其中可以有与object中同名的方法和默认方法(java8中接口方法可以有默认实现)。

2.java中函数式接口使用@functionalinterface进行注解。runnable、comparator都是函数式接口。

3.java.util.function包下为我们提供很多常用的函数式接口,例如function等。

用法举例:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

// 实现runnable中的run方法,替代匿名内部类。

runnable r = ()->system.out.print( "哈哈" );

// 作为参数传递。

new thread(()-> system.out.println( "haha" )).start();

 

arraylist<apple> list = new arraylist<>();

list.foreach(i-> system.out.println(i.getweight()));

 

 

// 简化策略模式

public static list<apple> filterapples(list<apple> inventory,applepredicate p){

  list<apple> apples = new arraylist<>();

  for (apple apple : inventory){

  if (p.test(apple)){

  apples.add(apple);

  }

  }

  return apples;

}

public class bigapple implement applepredicate{

  @override

  public boolean test(apple a){

  if (a.getweight> 10 ){

  return a

  }

  }

}

// 这是个简单的策略模式,根据用户的需要,创建不同的接口applepredicate实现类,调用时传入不同的实现类就可以,但问题是如果需求过多,创建的实现类也会很多,过于臃肿不方便管理。

xx.filterapple(inventory, new bigapple);

// 使用lambda表达式,不在需要创建bigapple类

xx.filterapple(inventory,i->(i.getweight> 10 ));

使用lambda表达式可以简化大量的模板代码,并且可以向方法直接传递代码。

总之

方法出参入参来自函数式接口

?

1

2

3

4

5

6

7

8

9

10

//入参s,返回void

(s)->system.out.println(s);

//入参空,返回void

()->system.out.print( "haha" );

//入参i,返回i+1

i->i+ 1

//后面写代码块

apple->{ if (apple.getweiht> 5 ) return "big" ;

  else return "small" ;

  }

好了,不多啰嗦了,如果感兴趣推荐下面的文章或《java8实战》的前三章。

1. Lambda表达式有何用处?如何使用?

2. java8实战

二、stream

流是什么?

java api的新成员,它允许你使用声明式方式处理数据集合(类似sql,通过查询语句表达,而不是临时编写一个实现)。

如果有人说lambda表达式不易于理解,那还勉强可以接受(其实过于复杂的lambda缺失不好阅读,但通常lambda不会做太复杂的实现),但流真的非常的易懂易用。这个语法糖真的是甜死了。

注意事项:

1.流只能使用一次,遍历结束就代表这个流被消耗掉了

2.流对集合的操作属于内部迭代,是流帮助我们操作,而不是外部迭代

3.流操作包含:数据源,中间操作链,终端操作三个部分。

基础流操作

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

list< double > collect = list.stream()

  // 过滤掉黑色的苹果

  .filter(i -> "black" .equals(i.getcolor()))

  // 让苹果按照重量个价格排序

  .sorted(comparator测试数据paring(apple::getweight)

  .thencomparing(i->i.getprice()))

  // 筛选掉重复的数据

  .distinct()

  // 只要苹果的价格

  .map(apple::getprice)

  // 只留下前两条数据

  .limit( 2 )

  // 以集合的形式返回

  .collect(tolist());

// 循环打印列表中元素

list.foreach(i->system.out.print(i));

apple::getprince<=>i -> i.getprince() 可以看做是仅涉及单一方法的语法糖,效果与lambda表达式相同,但可读性更好。

同理

下面列表为常见操作

中间

 

操作 类型 作用 函数描述 函数
filter 中间 过滤 t -> boolean predicate
sorted 中间 排序 (t,t)->int comparator
map 中间 映射 t->r function<t,r>
limit 中间 截断    
distinct 中间 去重,根据equals方法    
skip 中间 跳过前n个元素    

 

终端

 

操作 类型 作用
foreach 终端 消费流中的每个元素,使用lambda进行操作
count 终端 返回元素个数,long
collect 终端 将流归约成一个集合,如list,map甚至是integer

 

筛选与切片

?

1

2

3

4

5

6

7

8

9

10

list<string> strings = arrays.aslist( "hello" , "world" );

list<string> collect1 = strings.stream()

  // string映射成string[]

  .map(i -> i.split( "" ))

  // arrays::stream 数据数组,返回一个流string[]->stream<string>

  // flatmap各数组并不分别映射成一个流,而是映射成流的内容 stream<string>->stream

  .flatmap(arrays::stream)

  .collect(tolist());

system.out.println(collect);

----->输出 [h, e, l, l, o, w, o, r, l, d]

归约操作reduce

?

1

2

3

4

5

6

7

8

9

10

11

12

list<integer> integers = arrays.aslist( 12 , 3 , 45 , 3 , 2 ,- 1 );

// 有初始值的叠加操作

integer reduce = integers.stream().reduce( 3 , (i, j) -> i + j);

integer reduce2 = integers.stream().reduce( 5 , (x, y) -> x < y ? x : y);

// 无初始值的叠加操作

optional<integer> reduce1 = integers.stream().reduce((i, j) -> i + j);

// 无初始值的最大值

optional<integer> reduce4 = integers.stream().reduce(integer::min);

// 无初始值的最大值

optional<integer> reduce5 = integers.stream().reduce(integer::max);

// 求和

optional<integer> reduce6 = integers.stream().reduce(integer::sum);

reduce做的事情是取两个数进行操作,结果返回取下一个数操作,以次类推。

optional是java8引入的新类,避免造成空指针异常,在集合为空时,结果会包在optional中,可以用ispresent()方法来判断是否为空值。

无初始值的情况下可能为空,故返回optional

中间

 

操作 类型 作用 函数描述 函数
flatmap 中间 使通过的流返回内容 t -> boolean predicate

 

终端

 

操作 类型 作用
anymatch 终端 返回boolean,判断是否有符合条件内容
nonematch 终端 返回boolean,判断是否无符合条件内容
allmatch 终端 返回boolean,判断是全为符合条件内容
findany 终端 optional ,随机找一个元素返回
findfirst 终端 optional ,返回第一个元素
reduce 终端 optional  (t,t)->t 归约操作

 

数值流

包装类型的各种操作都会有拆箱操作和装箱操作,严重影响性能。所以java8为我们提供了原始数值流。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

// 数值流求平均值

optionaldouble average = apples.stream()

  .maptodouble(apple::getprice)

  .average();

// 数值流求和

optionaldouble average = apples.stream()

  .maptodouble(apple::getprice)

  .sum();

// 数值流求最大值,没有则返回2

double v = apples.stream()

  .maptodouble(apple::getprice)

  .max().orelse( 2 );

// 生成随机数

intstream s = intstream.rangeclosed( 1 , 100 );

下面列表为常见数值流操作操作

中间

 

操作 类型 作用
rangeclosed(1,100) 中间 生成随机数(1,100]
range(1,100) 中间 生成随机数(1,100)
boxed() 中间 包装成一般流
maptoobj 中间 返回为对象流
maptoint 中间 映射为数值流

 

终端,终端操作与list一般流类似

构建流

值创建

?

1

stream<string> s = stream.of( "java" , "python" );

数组创建

?

1

2

int [] i = { 2 , 3 , 4 , 5 };

stream< int > = arrays.stream(i);

由文件生成,nio api已经更新,以便利用stream api

?

1

stream<string> s = files.lines(paths.get( "data.txt" ),charset.defaultcharset());

由函数创建流:无限流

?

1

2

3

4

5

6

7

8

// 迭代

stream.iterate( 0 ,n->n+ 2 )

  .limit( 10 )

  .foreach(system.out::println);

// 生成,需要传递实现supplier<t>类型的lambda提供的新值

stream.generate(math.random)

  .limit( 5 )

  .foreach(system.out::println);

三、总结

至此,本文讲述了常见的流操作,目前排序、筛选、求和、归约等大多数操作我们都能实现了。与过去相比,操作集合变的简单多了,代码也变的更加简练明了。

目前vert.x,spring新出的webflux都通过lambda表达式来简化代码,不久的将来,非阻塞式框架的大行其道时,lambda表达式必将变的更加重要!

至于开篇见到的分组!!!下篇文章见~

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对的支持。

原文链接:https://HdhCmsTestcnblogs测试数据/cdream-zs/p/10504499.html

查看更多关于都9102年了,你还用for循环操作集合吗的详细内容...

  阅读:20次