流概述
获取流
流模型的操作方法
方法引用
流
流式思想概述
流式思想示意图
- 这里的filter、map、skip都只是对函数模型进行操作,并没有真正对集合元素处理,只有当最终方法count执行时,整个模型才会按照指定策略执行,这得益于lambda的延迟执行特性
- Java中stream流并不会存储元素,而是按需计算
![在这里插入图片描述](https://i-blog.csdnimg.cn/blog_migrate/b70b386a41e28be6ed417dab5a82806d.png#pic_center)
获取流
- 1、所有的Collection集合都可以通过stream默认方法获取流
- 2、Stream接口静态方法of可以获取数组对应的流
- Stream流是jdk1.8之后出现的
- 不关注怎么做,而关注做什么
import java.util.*;
import java.util.stream.Stream;
/**
* java.util.stream.Stream<T>是Java8新加入的常用接口(这不是一个函数式接口)
* 获取流的常用方式:
* 1、所有的Collection集合都可以通过stream默认方法获取流
* default Stream<E> stream()
* 2、Stream接口静态方法of可以获取数组对应的流
* static <T> Stream<T> of(T...value)
* 参数是一个可变参数,这这我们可以传递一个数组
*/
public class Demo02GetStream {
public static void main(String[] args) {
//把集合转换为Stream流
List<String> list = new ArrayList<>();
Stream<String> stream = list.stream();
Set<String> set = new HashSet<>();
Stream<String> stream1 = set.stream();
Map<String, String> map = new HashMap<>();
//获取键存储到set集合
Set<String> set1 = map.keySet();
Stream<String> stream2 = set1.stream();
//获取值
Collection<String> collection = map.values();
Stream<String> stream3 = collection.stream();
//获取键值对映射到entrySet集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
Stream<Map.Entry<String, String>> stream4 = entrySet.stream();
//把数组转换为stream流
Stream<Integer> stream5 = Stream.of(1,2,3,4);
Integer[] arr = {1,2,34};
Stream<Integer> stream6 = Stream.of(arr);
}
}
流模型的操作方法
延迟方法
- 返回值类型仍是Stream接口的自身类型方法,因此支持链式调用
终结方法
- 返回值类型不再是Stream接口的自身类型方法,因此不再 支持链式调用
forEach方法
import java.util.stream.Stream;
/**
* Stream流中常用方法foreach
* 该方法接收一个Consumer接口类型函数
* 简单记:
* forEach方法,用来遍历流中的数据
* 是一个终结方法,用来之后就不能遍历其它方法
*/
public class Demo01ForEach {
public static void main(String[] args) {
Stream<String> stream = Stream.of("小明", "小马", "xiaohua");
//遍历
stream.forEach(name-> System.out.println(name));
}
}
filter方法
- Stream流中filter方法,对流中数据进行过滤
- filter方法参数Predicate是一个函数式接口
import java.util.stream.Stream;
/**
* Stream流中filter方法,对流中数据进行过滤
* filter方法参数Predicate是一个函数式接口
*/
public class Demo01Filter {
public static void main(String[] args) {
Stream<String> stream = Stream.of("小明", "小马", "xiaohua");
//过滤
Stream<String> stream1 = stream.filter((name)->name.startsWith("小"));
//遍历
stream1.forEach(name-> System.out.println(name));
/**
* 注意事项:
* Stream流属于管道流,只能使用一次自动关闭
* 第一个Stream流调用完毕,数据就会转到下一个Stream上
* 这时第一个Stream流使用完毕,就会关闭
* 所以第一个Stream流就不能再调用方法
*/
}
}
映射map方法
- 如果要将流中的元素映射到另一个流中,可以使用map方法
- 该接口选择一个Function 函数式接口作为参数
import java.util.stream.Stream;
/**
* r 如果要将流中的元素映射到另一个流中,可以使用map方法
* 该接口选择一个Function 函数式接口作为参数
*/
public class Demo01Map {
public static void main(String[] args) {
Stream<String> stream = Stream.of("1", "2", "3", "4");
//使用映射,把字符串类型转换为整数
Stream<Integer> map = stream.map(value->Integer.parseInt(value));
//遍历
map.forEach(i-> System.out.println(i));
}
}
统计个数:count
- Stream 流中count方法,用于统计Stream流中元素的个数
- count方法是一个终结方法,返回值是一个long类型的整数
- 不能再调用stream流中其它方法
import java.time.temporal.JulianFields;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
/**
* Stream 流中count方法,用于统计Stream流中元素的个数
* long count()
* count方法是一个终结方法,返回值是一个long类型的整数
* 不能再调用stream流中其它方法
*/
public class Demo01Count {
public static void main(String[] args) {
List<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);
}
}
取前几个limit
- Stream流中limit方法,用于截取流中元素
- limit方法可以可以对流进行截取,只能取前n个
- Stream limit(long maxsize) 如果集合当前长度大于参数则进行截取,否则不做处理
import java.util.stream.Stream;
/**
* Stream流中limit方法,用于截取流中元素
* limit方法可以可以对六进行截取,只能取前n个
* Stream<T> limit(long maxsize) 如果集合当前长度大于参数则进行截取,否则不做处理
* limit方法是一个延迟方法,只是对流中的元素进行截取,所以可以继续调用Stream流中的其它方法
*/
public class Demo01Limit {
public static void main(String[] args) {
String[] arr = {"小狗", "小猫", "大王"};
Stream<String> stream = Stream.of(arr);
//使用limit方法对流进行截取
Stream<String> stream1 = stream.limit(2);
//遍历
stream1.forEach(name-> System.out.println(name));
}
}
跳过前几个skip
- Stream流中skip方法用于跳过元素
- 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流
- 如果当前长度大于参数n,则跳过前n个,否则会得到一个长度为0的新流
import java.util.stream.Stream;
/**
* Stream流中skip方法用于跳过元素
* 如果希望跳过前几个元素,可以使用skip方法获取一个截取之后的新流
* Stream<T> skip(long n)
* 如果当前长度大于参数n,则跳过前n个,否则会得到一个长度为0的新流
*/
public class Demo01Skip {
public static void main(String[] args) {
String[] arr = {"小狗", "小猫", "大王"};
Stream<String> stream = Stream.of(arr);
//使用skip跳过前1个元素
Stream<String> stream1 = stream.skip(1);
//遍历
stream1.forEach(name-> System.out.println(name));
}
}
concat连接多个流
- Stream流中concat方法把多个流合并乘一个流
/**
* Stream流中concat方法把俩个流合并乘一个流
*
*/
public class Demo01Concat {
public static void main(String[] args) {
String[] arr = {"小狗", "小猫", "大王"};
Stream<String> stream = Stream.of(arr);
String[] arr2 = {"马龙", "张继科", "樊振东"};
Stream<String> stream1 = Stream.of(arr2);
//把俩个流合并成一个流
Stream<String> stream2 = Stream.concat(stream, stream1);
//遍历
stream1.forEach(name-> System.out.println(name));
}
}
方法引用
- :: 被称为方法引用运算符,而它所在的表达式被称为方法引用
/**
* 定义一个打印的函数式接口
*/
@FunctionalInterface
public interface Printable {
void print(String s);
}
/**
*
*/
public class Demo01Printable {
/**
* 定义一个方法,传递Printable接口,对字符串打印
*/
public static void printString(Printable p) {
p.print("hello");
}
public static void main(String[] args) {
//调用printable方法,传递lambda表达式
printString(s-> System.out.println(s));
/**
* 分析:
* lambda表达式目的:打印参数传递的字符串
* 把参数s传递给了System.out对象,调用out方法中的println对字符串输出
* 注意:
* System.out 对象是已经存在的
* println 方法也是存在的
* 所以可以直接使用System.out方法直接引用println方法
* :: 被称为方法引用运算符,而它所在的表达式被称为方法引用
* 如果lambda要表达的函数方案已经存在于某个方法的实现中,那么可以通过::来引用该方法作为lambda表达式的替代者
* 区别:
* 第一种lambda语义:拿到参数之后经过lambda表达式之手,进而传递给System.out.println方法去处理
* 第二种语义,直接让System.out的println方法去替代lambda表达式
* 注意:lambda中传递的参数一定是方法引用中那个方法可以接受的类型,否则会抛出异常
*/
printString(System.out::println);
}
}
通过对象引用成员方法
- 通过对象来引用成员方法前提是对象名已经存在,成员方法已经存在
/**
* 定义一个打印的函数式接口
*/
@FunctionalInterface
public interface Printable {
void print(String s);
}
public class Demo01MethodObject {
//定义一个成员方法,传递字符串并且按照大写输出
public void printUpperCaseString(String s) {
System.out.println(s.toUpperCase());
}
}
import com.thread.demo12waitandnotify.Demo;
/**
* 通过对象来引用成员方法
* 前提是对象名已经存在,成员方法已经存在
*/
public class Demo01ObjectMethodReference {
public static void printString(Printable p) {
p.print("hello");
}
public static void main(String[] args) {
//调用上述方法,传递一个lambda表达式
printString((s)-> {
//创建一个MethodObject对象
Demo01MethodObject obj = new Demo01MethodObject();
//调用成员方法,按照大写输出
obj.printUpperCaseString(s);
});
/**
* 方法优化
* 对象存在
* 成员方法存在
* 所以可以通过对象名引用成员方法
*/
Demo01MethodObject obj = new Demo01MethodObject();
printString(obj::printUpperCaseString);
}
}
通过类名引用静态成员方法
- 通过类名引用静态成员方法前提类已经存在,静态成员方法也已经存在
public interface Calcable {
/**
* 定义一个抽象方法,传递一个整数,返回绝对值
*/
int calAbs(int num);
}
/**
* 通过类名引用静态成员方法
* 类已经存在,静态成员方法也已经存在
* 可以通过类名引用静态成员方法
*/
public class Demo01StaticMethodReference {
//定义一个方法,传递一个参数,另一个参数类型为接口
public static int method(int num, Calcable c) {
return c.calAbs(num);
}
public static void main(String[] args) {
//调用方法计算绝对值
int num = method(-10, (n)-> {
return Math.abs(n);
});
System.out.println(num);
/**
* 使用方法优化lambda表达式
* Math类是存在的
* abs计算绝对值得警惕方法也是存在的
* 所以可以通过类名调用成员方法
*/
int num1 = method(-10, Math::abs);
System.out.println(num1);
}
}
通过super调用父类的成员方法
- 使用super引用父类的成员方法前提super已经存在父类的成员方法sayHello已经存在
public interface Greetable {
//定义一个见面的方法
void greet();
}
/**
* 定义父类
*/
public class Human {
public void sayHello() {
System.out.println("hello 我是Human");
}
}
/**
* 定义子类
*/
public class Man extends Human {
//子类重写父类方法
@Override
public void sayHello() {
System.out.println("hello 我是man");
}
//定义一个方法传递Greetable接口
public void method(Greetable g) {
g.greet();
}
public void show() {
//调用method方法
method(()-> {
//创建父类对象
Human h = new Human();
//调用父类方法
h.sayHello();
});
//因为有子父类,所以存在一个关键字,代表父类,可以直接使用super调用父类的成员方法
method(()-> {
super.sayHello();
});
/**
* 使用super引用父类的成员方法
* super已经存在
* 父类的成员方法sayHello已经存在
* 所以可以直接使用super引用父类的成员方法
*/
method(super::sayHello);
}
public static void main(String[] args) {
new Man().show();
}
}
引用本类的成员方法
- 引用本类的成员方法前提this存在本类成员方法也存在
/**
* 定义一个富有的函数式接口
*/
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() {
//调用结婚的方法,参数传递一个lambda表达式
marry(()-> {
//调用本类的成员方法
this.buyHouse();
});
/**
* 使用方法引用优化lambda表达式
* this存在
* 本类成员方法也存在
* 所以可以通过this引用本类的成员方法
*/
marry(this::soHappy);
}
public static void main(String[] args) {
new Husband().soHappy();
}
数组构造器的引用
- 数组构造器的引用前提已知创建的是int[]数组,数组的长度已知
/**
* 定义一个创建数组的函数式接口
*/
public interface ArrayBuilder {
//定义一个创建int类型数组的方法
int[] builderArray(int length);
}
/**
* 数组的构造器引用
*/
public class Demo {
/**
* 定义一个方法
* 方法参数传递创建数组的长度和Arraybuilder接口
*/
public static int[] createArray(int length, ArrayBuilder ab) {
return ab.builderArray(length);
}
public static void main(String[] args) {
//调用上述方法创建数组
int[] arr = createArray(10, (len)-> new int[len]);
System.out.println(arr.length);
/**
* 使用lambda表达式优化
* 已知创建的是int[]数组
* 数组的长度已知
* 可以使用方法引用来创建数组
*/
int[] arr2 = createArray(20,int[]::new);
System.out.println(arr2.length);
}
}
类的构造器引用
/**
* 定义一个创建Person对象的函数式接口
*/
@FunctionalInterface
public interface PersonBuilder {
//定义一个方法,根据传递的姓名返回对象
Person builderPerson(String name);
}
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;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
'}';
}
}
/**
* 类的构造器引用
*/
public class Demo {
//定义一个方法,根据传递字符串返回对象
public static void printName(String name, PersonBuilder pb) {
Person person = pb.builderPerson(name);
System.out.println(person);
}
public static void main(String[] args) {
//调用构造对象的安方法
printName("张继科", name -> new Person(name));
/**
* 使用优化的lambda表达式
* 构造方法new Person(String name) 已知
* 创建对象已知
* 可以使用其创建对象
*/
printName("马龙", Person::new); //使用Person类的带参构造方法,通过传递姓名创建对象
}
}