1.Stream流

1.for循环带来的弊端

  • 在jdk8中,lambda专注于做什么,而不是怎么做
  • for循环的语法就是怎么做
  • for循环的循环体才是做什么

遍历是指每一个元素逐一进行处理,而并不是从第一个到最后一个顺次处理的循环。前者是目的,后者是方式。

集合存储案列:

import java.util.ArrayList;
import java.util.List; public class Demo{
public static void main(String[] args){
// 创建一个list集合,存储姓名
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("赵强");
list.add("张三丰"); //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
list<String> listA = new ArrayList<>();
for(String str:list){
if(str.startsWidth("张")){
listA.add(str);
}
}
//对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
List<String> ListB = new ArrayList<>();
for(String s:listA){
if(s.length()==3){
listB.add(s);
}
}
}
}

2.使用Stream的更优写法

    import java.util.ArrayList;
import java.util.List; public class Demo{
public static void main(String[] args){
// 创建一个list集合,存储姓名
List<String> list = new ArrayList<>();
list.add("张无忌");
list.add("周芷若");
list.add("赵敏");
list.add("赵强");
list.add("张三丰"); list.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
}

流思想

Stream(流)是一个来自数据源的元素队列

  • 元素是特定类型的对象,形成一个队列。Java中的Stream并不会存储元素,二十按需计算。
  • 数据源流的来源,可以是 集合, 数组等。

和以前的Collection操作不同,Stream操作还有两个基础的特征:

  • Pipelining:中间操作都会返回流对象本省。着多个操作可以串联一个管道,如同流式风格,可以对操作进行优化, 比如延迟执行(laziness)和短路(short-circuiting)。

  • 内部迭代:以前对集合变量都是通过Iterator或者是增强for循环的方式,显示在集合外部进行迭代,这种就是外部迭代。Stream提供了内部迭代的方式,流可以直接调用遍历方法。

  • 使用流的步骤

    获取一个数据源(source)→ 数据转换→执行操作获取想要的结 果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以 像链条一样排列,变成一个管道。

3.获取流

获取一个流的方法非常简单,有以下几种常用的方式:

  • 所有的Collection集合都可以通过Stream默认方法获取流;

    • default Stream<E> stream()
  • Stream接口的静态方法of可以获取数据对应的流。
    • static <T> Stream<T> of(T.. values)
    • 参数是一个可变参数,那么可以传递一个数组
    public class Demo{
public static void main(String[] args){
// 把集合转为Stream流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream(); Set<String> set = new HashSet<>();
Stream<String> stream2 = set.stream(); Map<String,String> map = new HashMap<>();
//获取键,春初到Set集合中
Set<String> KeySet = map.KeySet();
Stream<String> stream3 = KeySet.stream();
//获取值,存储带一个Collection集合中
Collection<String> values = map.values();
Stream<String> stream4 = values.stream();
//获取键值对(键与值的映射关系 entrySet)
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream5 = entries.stream();
//把数组转换为Stream流
Stream<Integer> stream6 = Stream.of(1,2,3,4,5,6);
//可变参数传递数组
Integer[] arr = {1,2,3,4,5,6};
Stream<Integer> stream7 = Stream.of(arr);
Strng[] arr2 = {"a", "bn", "cd"};
Stream<String> stream8 = Stream.of(arr2);
}
}

4.常用方法

forEach方法

Stream流常用方法 forEach

  • void forEach(Consumer<? super T> action):
  • 该方法节后一个Consumer接口函数,会将每一个流元素交给该函数进行处理。
  • Consumer接口是一个消费型的函数式接口, 可以传递Lambda表达式,消费数据

简单记:

  • forEach方法,用来遍历流中的数据
  • 是一个终结方法,遍历之后就不能继续调用Stream流中的其他方法
    public class Demo{
public class void main(String[] args){
// 获取一个Stream流
Stream<String> stream = Stream.of("张三","李四","王五","赵六","田七");
// 使用stream流中的方法forEach对Stream流中的数据进行遍历
/* stream.forEach((String name)->{
System.out.println(name);
});*/
stream.forEach(name->system.out.println(name));
}
}

filter方法

用于对Stream流中的数据进行过滤

  • Stream<T> filter(Predicate<? super T> predicate);
  • filter方法中的参数Predicate是一个函数式接口,所以可以传递Lambda表达式,对数据进行过滤
  • Predicate中的抽象方法:
    • boolean test(T t)
    public class Demo{
public static void main(String[] args){
// 创建一个Stream流
Stream<String> stream = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
// 对Stream中的元素进行过滤,只要姓张的人
Stream<String> stream2 = stream.filter((String name)->{return name.stratsWith("张");});
// 遍历Stream2
stream2.forEach(name->System.out.println(name));
/*
Stream流属于管道流,只能被消费(使用)一次
第一个Stream流调用完毕方法,数据就会流转到下一个Stream上
而这时第一个Stream流已经使用完毕,就会关闭了
所以第一个Stream流就不能再调用方法了
IllegalStateException: stream has already been operated upon or closed
*/
// 遍历stream流
stream.forEach(name-> System.out.println(name));
}
}

map方法

用于类型转换

  • 如果需要将流中的元素映射到另一个流中,可以使用map方法
  • <R> Stream<R> map(Function <? super T, ? extends R> mapper);
  • 该接口需要一个Function函数式接口参数,可以将当前流中的T类型转换为另一种R数据的流
  • Function的抽象方法
    • R apply(T t);
    public class Demo{
public static void main(String[] args){
// 获取一个String类型的Stream流
Stream<String> stream = Stream.of("1", "2", "3", "4");
// 使用map方法,把字符串类型的整数,转换为Integer类型的整数
Stream<Integer> stream2 = stream.map((String s)-> {return Integer.parseInt(s);});
// 变量stream2
stream2.forEach(i -> System.out.println(i));
}
}

count 方法

用于统计Stream流中元素的个数

  • long count();
  • count 方法是一个终结方法,返回值是一个long类型的整数,只能使用一次,使用完之后,不可以使用流的其他方法
    public class Demo{
public static void main(String[] args){
//获取一个Stream流
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
Stream<Integer> Stream = list.stream();
long count = stream.count();
System.out.println(count); // 4
}
}

limit方法

用于截取流中的元素

  • limit方法可以对流进行截取,支取前n个
  • Stream<T> limit(long maxSize);
    • 参数是一个int类型。如果当前的长度大于参数则进行截取, 否则不进行截取
  • limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法
    public class Demo{
public static void main(String[] args){
// 获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream = Stream.of(arr);
// 使用limit放啊进行截取,只要前三个元素
Stream<String> stream2 = stream.limit(3);
// 遍历操作
stream2.forEach(name->System.out.println(name));
}
}

skip方法

用于跳过元素,截取后面的元素

  • 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流
  • Stream<T> skip(long n);
    • 如果流的当前长度大于n,则跳过钱n个, 否则得到一个长度为0的空流.
    public class Demo{
public static void main(String[] args){
// 获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream = Stream.of(arr);
// 使用skip方法跳过前面三个元素
Stream<String> stream2 = stream.skip(3);
// 遍历stream2
stream2.forEach(name-> System.out.println(name));
}
}

concat方法

用于把流组合在一起

  • 如果有两个流,希望合并成一个流,那么可以使用Stream接口的静态方法concat
  • static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T>b)
    public class Demo{
public static void main(String[] args){
//创建一个Stream流
Stream<String> stream1 = Stream.of("张三丰", "张翠山", "赵敏", "周芷若", "张无忌");
// 获取一个Stream流
String[] arr = {"美羊羊","喜洋洋","懒洋洋","灰太狼","红太狼"};
Stream<String> stream2 = Stream.of(arr);
// 把以上两个流组成为一个流
Stream<String> concat = Stream.concat(stream1, stream2);
concat.forEach(name->System.out.println(name));
}
}

2.方法引用

1.冗余的Lambda

定义一个打印的函数式接口

@FunctionalInterface
public interface Printable {
//定义字符串的抽象方法
void print(String s);
}

打印字符串

public class Demo{
// 定义一个方法,参数传递的是Printable的接口,对字符串进行打印
public static void printString(Printable p){
p.print("hello world");
}
public static void main(String[] args){
//调用printString方法,方法的参数Printable是一个函数式的接口,所以可以传递lambda函数
printString((s)->{
System.out.println(s);
});
/*
分析:
Lambda表达式的目的,打印参数传递的字符串
把参数s,传递给了System.out对象,调用out对象中的方法println对字符串进行了输出
注意:
1.System.out对象是已经存在的
2.println方法也是已经存在的
所以我们可以使用方法引用来优化Lambda表达式
可以使用System.out方法直接引用(调用)println方法
*/
printString(System.out::println);
}
}

2.分析

这段代码的问题在于,对字符串进行控制台打印输出的操作方案,明明已经有了现成的实现,那就是 System.out 对象中的 println(String) 方法。既然Lambda希望做的事情就是调用 println(String) 方法,那何必自己手动调 用呢?

3.使用方法引用

public class Demo{
public static void printString(Printable p){
p.print("Hello world");
}
public static void main(String[] args){
// 调用方法使用方法的引用
printString(System.out::println);
}
}

4.方法引用符

双冒号:: 为引用运算符,称为方法引用。如果Lambda要表达的函数方案已经存在于某个方法的实现中,name我们可以使用双冒号来引用该方法作为lambda的替代者。

语法分析

例如上例中, System.out 对象中有一个重载的 println(String) 方法恰好就是我们所需要的。那么对于

printString 方法的函数式接口参数,对比下面两种写法,完全等效:

  • Lambda表达式写法: s -> System.out.println(s);
  • 方法引用写法: System.out::println

第一种语义是指:拿到参数之后经Lambda之手,继而传递给 System.out.println 方法去处理。

第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代Lambda。两种写法的执行效果完全一

样,而第二种方法引用的写法复用了已有方案,更加简洁。

注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常

5.通过对象名引用成员方法

定义类

public class MethodRefObject{
public void printUpperCase(String str){
System.out.println(str.toUpperCase());
}
}

函数式接口定义

@FunctionalInterface
public interface Printable{
void print(String str);
}

那么当需要使用这个printUpperCase成员方法来代替Printable接口的Lambda的时候,已经具有了MethodRefObject类的对象实例。则可以通过对象名引用成员方法,代码:

/*
通过对象名引用成员方法
使用前提:
对象名是已经存在的,成员方法也存在
*/
public class Demo{
// 定义一个方法,方法的参数传递Printable接口
public static void printString(Ptrintable p){
p.print("hello");
}
public static void main(String[] args){
// 创建MethodRerObject对象
MethodRerObject obj = new MethodRerObject();
printString(obj::printUpperCaseString);
}
}

6.通过类名称引用静态方法

由于在java.lang.Math类中已经存在存在了静态方法abs,所以当我们需要通过Lambda来调用该方法时,有两种写法。

函数式接口:

@FunctionalInterface
public interface Calcable{
int calc(int num);
} public class Demo{
public static int method(int number, Calcable c){
return c.calsAbs(number);
}
public static void main(String[] args){
// 调用method方法,传递计算绝对值
int number = method(-10, Math::abs);
System.out.println(number2);
}
}

7.通过super引用成员方法

定义见面的函数式接口

@FunctionalInterface
public interface Greetable{
//定义一个见面的方法
void greet();
}

定义父类

public class Human{
//定义一个sayHello的方法
public void sayHello(){
System.out.println("hello,我是Human");
}
}

定义子类

public class Man extends Human{
//子类重写父类的sayHello方法
@Override
public void sayHello(){
System.out.println("hello 我是Man");
}
//定义一个方法参数传递Greetable接口
public void method(Greetable g){
g.greet();
}
public void show(){
method(super::sayHello);
}
public static void main(String[] args){
new Man().show();
}
}
// hello 我是Human

8.通过this引用成员方法

定义购买的函数式接口

@FunctionalInterface
public interface Richable{
// 定义一个想买什么就买什么的方法
void buy();
}

定义一个类

使用this引用本类的成员方法

public class Husband{
// 定义一个买房子的方法
public void buyHouse(){
System.out.println("北京二环买一套四合院");
}
// 定义一个结婚的方法参数传递Richable接口
public void marry(Richable r){
r.buy();
}
//定义一个非常高兴的方法
public void soHappy(){
marry(this::buyHouse);
}
public static void main(String[] args){
new Husband().soHappy();
}
}

9.类的构造器引用

由于构造器的名称与类名完全一样,并不固定。所以构造器引用使用 类名称::new 的格式表示

定义Person类

public class Person{
private String name;
public Person(String name){
this.name=name;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
}

Person对象的函数式接口

@FunctionalInterface
public interface PersonBuilder{
Person builderPerson(String name);
}

Demo

public class Demo{
//定义一个方法,传递的是姓名和PersonBuilder接口,方法中通过姓名创建Person对象
public static void printName(String name, PersonBuilder pb){
Person person = pb.builderPersom(name);
System.out.println(person.getName());
}
public static void main(String[] args){
/*构造方法new Person(String name) 已知
创建对象已知 new
就可以使用Person引用new创建对象*/
//使用Person类的带参构造方法,通过传递的姓名创建对象
printName("古力娜扎", Person::new);
}
}

10.数组的构造器引用

数组也是 Object 的子类对象,所以同样具有构造器,只是语法稍有不同。

定义一个创建数组的函数式接口

@FunctionalInterface
public class ArrayBuilder{
//定义一个创建int类型数组的方法,参数传递的是数组的长度,返回创建的int类型数组
int[] builderArray(int length);
}

Demo

public class Demo{
/*
定义一个方法
方法的参数传递创建数组的长度和ArrayBuilder接口
方法内部根据传递的长度使用ArrayBuilder中的方法创建数组并返回
*/
public static int[] createArray(int length, ArrayBuilder ab){
return ab.builderArray(length);
}
public static void main(String[] args){
/*
使用方法引用优化Lambda表达式
已知创建的就是int[]数组
数组的长度也是已知的
就可以使用方法引用
int[]引用new,根据参数传递的长度来创建数组
*/
int[] array = createArray(10, int[]::new);
System.out.println(Arrays.toString(array));
System.out.println(array.length);
}
}

Stream和方法引用的更多相关文章

  1. Stream流方法引用

    一.对象存在,方法也存在,双冒号引用 1.方法引用的概念: 使用实例: 1.1先定义i一个函数式接口: 1.2定义一个入参参数列表有函数式接口的方法: 1.3调用这个入参有函数式接口的方法: lamb ...

  2. Java 8新特性:新语法方法引用和Lambda表达式及全新的Stream API

    新语法 方法引用Method references Lambda语法 Lambda语法在AndroidStudio中报错 Stream API 我正参加2016CSDN博客之星的比赛 希望您能投下宝贵 ...

  3. JavaSE复习(七)Stream流和方法引用

    Stream流 全新的Stream概念,用于解决已有集合类库既有的弊端. 传统集合的多步遍历代码 几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作.而当我们 ...

  4. Stream流、方法引用

    Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 Stream流.方法引用 ... ...

  5. Java8特性之Lambda、方法引用以及Stream流

    Java 8 中的 Streams API 详解:https://www.ibm.com/developerworks/cn/java/j-lo-java8streamapi/ Java笔记——Jav ...

  6. JDK1.8新特性(一) ----Lambda表达式、Stream API、函数式接口、方法引用

    jdk1.8新特性知识点: Lambda表达式 Stream API 函数式接口 方法引用和构造器调用 接口中的默认方法和静态方法 新时间日期API default   Lambda表达式     L ...

  7. 2020你还不会Java8新特性?方法引用详解及Stream 流介绍和操作方式详解(三)

    方法引用详解 方法引用: method reference 方法引用实际上是Lambda表达式的一种语法糖 我们可以将方法引用看作是一个「函数指针」,function pointer 方法引用共分为4 ...

  8. java基础第11期——Stream流、方法引用、junit单元测试

    1.Stream流 Stream流与io流是不同的东西,用于解决集合类库已有的弊端, 1.1 获取Stream流: Collection集合的Stream方法,注意Map集合要经过转化 default ...

  9. Java8新特性代码示例(附注释)- 方法引用,Optional, Stream

    /** * java8中的函数式接口,java中规定:函数式接口必须只有一个抽象方法,可以有多个非抽象方法,同时,如果继承实现了 * Object中的方法,那么也是合法的 * <p> * ...

随机推荐

  1. NetCore AutoMapper的封装

     需要引用AutoMapper的Nuget包  如果需要忽略某个字段就在字段上面打标签如下:  [IgnoreMap] public string IgnoreValue { get; set; } ...

  2. Python-入门学习

    开始学习Python(围绕windows 平台上),记录一些点滴的知识点,也希望可以和大家交流分享. ================================================= ...

  3. Java多线程(七):ReentrantLock

    加锁和解锁 我们来看下ReentrantLock的基本用法 ThreadDomain35类 public class ThreadDomain35 { private Lock lock = new ...

  4. python面向过程编程小程序- 模拟超市收银系统

    6.16自我总结 功能介绍 程序功能介绍: 商品信息再读取修改买卖均已xlsx格式 且生成购物记录也按/用户名/购买时间.xlsx格式生成 账号密码输入错误三次按照时间进行冻结 用户信息已json格式 ...

  5. SGU495 Kids andPrices[期望DP]

    也许更好的阅读体验 \(\mathcal{Description}\) 有\(n\)个格子,每次等概率随机给一个格子染色,问涂\(m\)次后期望有多少格子被染色了 \(\mathcal{Solutio ...

  6. sonarlint在idea&eclipse中安装与使用

    介绍 ​ 代码质量管理的开源平台,用于管理源代码的质量 通过插件形式,可以支持包括java,C#,C/C++,PL/SQL,Cobol,JavaScrip,Groovy等等二十几种编程语言的代码质量管 ...

  7. APP爬虫(2)把小姐姐的图片down下来

    APP爬虫(1)想学新语言,又没有动力,怎么办? 书接上文.使用appium在帖子列表界面模拟上划动作,捕捉不到列表的规律.上划结束后,列表只能获取到屏幕呈现的部分,而且下标还是从0开始的. 根据酸奶 ...

  8. PHP中的$_GET变量

    定义 在 PHP 中,预定义的 $_GET 变量用于收集来自 method="get" 的表单中的值. $_GET 变量 预定义的 $_GET 变量用于收集来自 method=&q ...

  9. python课堂整理5---元组

    一.元组   Tuple tu = (111, 22, 33, "alex", (11,22), [(33, 44)], True, ) 元组元素不可被修改,不能被增加或删除 一般 ...

  10. k8s1.9.0安装--基础集群部署

    二.基础集群部署 - kubernetes-simple 1. 部署ETCD(主节点) 1.1 简介 kubernetes需要存储很多东西,像它本身的节点信息,组件信息,还有通过kubernetes运 ...