栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

06-Stream API 操作篇

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

06-Stream API 操作篇

若想了解java8其它新特性,请到 00-java8常用新特性文章索引 阅读。

这篇文章写Stream API 的使用,各个用例中会大量结合 lambda 表达式,如果 函数式接口lambda表达式 玩得不熟的朋友先看看 01-函数式接口和 lambda 表达式 ,如果对 Stream API 的创建、中间操作、终止操作相关接口不熟悉的朋友先看看 05-Stream API 第一篇。

提示: 表格的排版在手机端阅读可能有点乱,表格内容可以尝试手机横屏阅读或者电脑端阅读。

Java常用的内置函数式接口:

函数式接口 参数类型 返回类型 用途
Consumer 消费型接口 T void 对给定的 T 类型的参数,进行操作,
核心方法:void accept(T t);
Supplier 供给型接口 空参 T 获取一个 T 类型的对象
核心方法:T get();
Function 函数型接口 T R 对 T 类型的参数进行操作,返回 R 类型的结果
核心方法:R apply(T t);
Predicate 断定型接口 T boolean 判断 T 类型的参数,是否满足某约束,返回 boolean 值
核心方法:boolean test(T t);
BiFunction T, U R 对类型为 T, U 参数应用操作,
返回 R 类型的结果。
核心方法为 R apply(T t, U u);
UnaryOperator
(Function子接口)
T T 对类型为T的对象进行一元运算,
并返回T类型的结果。
核心方法为T apply(T t);
BinaryOperator
(BiFunction 子接口)
T, T T 对类型为T的对象进行二元运算,
并返回T类型的结果。
核心方法为T apply(T t1, T t2);
BiConsumer T, U void 对类型为T, U 参数应用操作。
核心方法为void accept(T t, U u)
ToIntFunction
ToLongFunction
ToDoubleFunction
T int/long/double T 类型的参数,
返回intlongdouble 类型的结果,
核心方法为intlongdouble applyAsInt(T value);
IntFunction
LongFunction
DoubleFunction
int/long/double R 参数分别为int、long、double 类型的函数,
返回 R 类型的结果,
核心方法: R apply(intlongdouble value);
1 Stream 创建 API

Stream创建:可通过 数组、Collction 和 Steam类的静态方法来创建一个Stream实例。

定义 Person ,在下面大部分测试用例用到的实体 Bean:

public class Person {

    //姓名
    private String name;
    //年龄
    private int age;

    public Person(String name, int age) {
 this.name = name;
 this.age = age;
    }

    public String getName() {
 return name;
    }

    public void setName(String name) {
 this.name = name;
    }

    public int getAge() {
 return age;
    }

    public void setAge(int age) {
 this.age = age;
    }

    @Override
    public String toString() {
 return "Person{" +
  "name='" + name + ''' +
  ", age=" + age +
  '}';
    }

    @Override
    public boolean equals(Object o) {
 if (this == o) return true;
 if (!(o instanceof Person)) return false;
 Person person = (Person) o;
 return getAge() == person.getAge() &&
  Objects.equals(getName(), person.getName());
    }

    @Override
    public int hashCode() {

 return Objects.hash(getName(), getAge());
    }

}
1.1 通过数组创建

    
    @Test
    public void testCreateByArray() {
 int[] nums = new int[]{0, 1, 5, 2, 4, 8, 9};
 Person[] personArray = new Person[]{
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15)
 };
 // Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
 IntStream stream = Arrays.stream(nums);
 Stream personStream = Arrays.stream(personArray);

 //打印 stream 中的元素
 System.out.println("打印 stream 中的元素: ");
 stream.forEach(item -> System.out.print(item + " "));
 //打印 personStream 中的元素
 System.out.println("n打印 personStream 中的元素: ");
 personStream.forEach(item -> System.out.println(item + " "));
    }

运行结果:

打印 stream 中的元素: 
0 1 5 2 4 8 9 
打印 personStream 中的元素: 
Person{name='张三', age=24} 
Person{name='李四', age=29} 
Person{name='王五', age=22} 
Person{name='赵六', age=15} 
1.2 通过Collection创建

Collection接口中提供了 stream() 获取顺序流和 parallelStream() 获取并行流的两个方法
顺序流:串行流,流中的元素序列顺序和元素在数据源中的顺序是一样的。
并行流:把元素序列分成多个数据块,并用不同的线程分别处理每个数据块的流

Stream API 可以通过 parallel() 与 sequential() 在并行流与顺序流之间进行切换。

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testCreateByCollection() {
 //stream() 将此集合作为数据源,返回一个顺序流
 Stream seqStream = persons.stream();
 //parallelStream() 将此集合作为数据源,返回一个并行流
 Stream parallelStream = persons.parallelStream();


 //打印顺序流 seqStream 中的元素
 System.out.println("打印顺序流 seqStream 中的元素: ");
 seqStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));


 //并行流就是把一个内容分成多个数据块,并用不同的线程分别处理每个数据块的流
 // 打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的
 System.out.println("n打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的: ");
 parallelStream.forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));

 System.out.println("nsequential()把并行流切换成顺序流,输出: ");
 persons.parallelStream()
  .sequential()//并行流切换成顺序流
  .forEach(item -> System.out.println("线程名" + Thread.currentThread().getName() + "--" + item + " "));
    }

}

运行结果:

打印顺序流 seqStream 中的元素: 
线程名main--Person{name='刘一', age=25} 
线程名main--Person{name='陈二', age=12} 
线程名main--Person{name='张三', age=24} 
线程名main--Person{name='李四', age=29} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='孙七', age=16} 
线程名main--Person{name='周八', age=18} 

打印并行流 parallelStream 中的元素,并行输出,输出结果是无序的: 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='周八', age=18} 
线程名main--Person{name='孙七', age=16} 
线程名ForkJoinPool.commonPool-worker-11--Person{name='李四', age=29} 
线程名ForkJoinPool.commonPool-worker-9--Person{name='张三', age=24} 
线程名ForkJoinPool.commonPool-worker-2--Person{name='陈二', age=12} 
线程名ForkJoinPool.commonPool-worker-9--Person{name='刘一', age=25} 

sequential()把并行流切换成顺序流,输出: 
线程名main--Person{name='刘一', age=25} 
线程名main--Person{name='陈二', age=12} 
线程名main--Person{name='张三', age=24} 
线程名main--Person{name='李四', age=29} 
线程名main--Person{name='王五', age=22} 
线程名main--Person{name='赵六', age=15} 
线程名main--Person{name='孙七', age=16} 
线程名main--Person{name='周八', age=18} 

1.3 通过Stream的静态方法创建Stream

Stream接口提供了几个静态方法可以创建一个Stream实例,常用的是:of()、iterate()、generate()


    
    @Test
    public void testCreateByStaticMethod() {
 // 将参数数组作为数据源,返回一个顺序流,底层调用的是Arrays.stream(T[] array)
 Stream of = Stream.of(0, 1, 5, 2, 4, 8, 9);// 不断产生 0,2,4,6,8,10.,...,n,n+2
 System.out.println("of() 将参数数组作为数据源,返回一个顺序流: ");
 of.forEach(item -> System.out.print(item + " "));

 // 创建无限流,入参是初始元素和UnaryOperator函数式接口,返回一个有规律的无限顺序流
 Stream iterate = Stream.iterate(0, n -> n + 2);// 不断产生 0,2,4,6,8,10.,...,n,n+2
 System.out.println("nniterate() 返回一个有规律的无限顺序流,限制输出前10个: ");
 iterate.limit(10).forEach(item -> System.out.print(item + " "));
 // 创建无限流,入参是Supplier,返回一个无规律的无限顺序流,其中每个元素由提供的Supplier生成,适用于生成恒定流、随机元素流
 Stream generate = Stream.generate(() -> (int) (Math.random() * 10));// 不断产生 0-9 的随机数
 System.out.println("nngenerate() 返回一个有规律的无限顺序流,限制输出前10个: ");
 generate.limit(10).forEach(item -> System.out.print(item + " "));
    }

运行结果:

of() 将参数数组作为数据源,返回一个顺序流: 
0 1 5 2 4 8 9 

iterate() 返回一个有规律的无限顺序流,限制输出前10个: 
0 2 4 6 8 10 12 14 16 18 

generate() 返回一个有规律的无限顺序流,限制输出前10个: 
2 6 9 2 7 1 4 6 0 0 
2 终止操作

因为 Stream 有惰性求值的特点,中间操作,并不会立即执行,只有触发终止操作时,才会进行实际的运算。

Stream 操作可以有 0个或者多个中间操作,但是 有且只有一个终止操作

所以下面为了方便演示,我们讲解终止操作的使用,再讲解中间操作的使用。

2.1 allMatch

allMatch操作,如果流中所有元素都匹配 predicate 或者是空流,返回true,否则返回false

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamAllMatch() {
 Stream emptyStream = Stream.empty();
 Stream stream = persons.stream();
 // 流中所有Person元素都大于20岁
 boolean allMathchGt20 = stream.allMatch(item -> item.getAge() > 20);
 System.out.println("流中所有Person元素都大于20岁: " + allMathchGt20);
 // 空流中所有Person元素都大于20岁
 boolean allMathchGt20InEmptyStream = emptyStream.allMatch(item -> item.getAge() > 20);
 System.out.println("空流中所有Person元素都大于20岁: " + allMathchGt20InEmptyStream);
    }
}    

运行结果:

流中所有Person元素都大于20岁: false
空流中所有Person元素都大于20岁: true
2.2 noneMatch

noneMatch 操作,如果流中没有元素匹配 predicate 或者是空流,返回true,否则返回false

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamNoneMatch() {
 Stream emptyStream = Stream.empty();
 Stream stream = persons.stream();
 // 流中没有Person元素大于30岁
 boolean noneMathchGt30 = stream.noneMatch(item -> item.getAge() > 30);
 System.out.println("流中没有Person元素大于30岁: " + noneMathchGt30);
 // 空流中所有Person元素都大于30岁
 boolean noneMathchGt30InEmptyStream = emptyStream.noneMatch(item -> item.getAge() > 30);
 System.out.println("空流中没有Person元素大于30岁: " + noneMathchGt30InEmptyStream);
    }
}    

运行结果:

流中没有Person元素大于30岁: true
空流中没有Person元素大于30岁: true
2.3 anyMatch

anyMatch 操作,如果流中任意一个元素匹配 predicate返回true,否则返回false,空流返回false

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamAnyMatch() {
 Stream emptyStream = Stream.empty();
 Stream stream = persons.stream();
 // 流中任意一个Person元素都小于18岁
 boolean anyMathchGt18 = stream.anyMatch(item -> item.getAge() > 18);
 System.out.println("流中任意一个Person元素都小于18岁: " + anyMathchGt18);
 // 空流中任意一个Person元素都小于18岁
 boolean anyMathchGt18InEmptyStream = emptyStream.anyMatch(item -> item.getAge() > 18);
 System.out.println("空流中任意一个Person元素都小于18岁: " + anyMathchGt18InEmptyStream);
    }
}    

运行结果:

流中任意一个Person元素都小于18岁: true
空流中任意一个Person元素都小于18岁: false
2.4 collect

collect操作:接收一个Collector实例,将流中元素收集成另外一个数据结构

2.4.1 介绍 Collector 和 Collectors

Collector 作为 Stream 的collect操作的参数类型,能够把流中的元素序列收集到另一个数据结构中。
Collectors是工具类,提供了很多静态方法用于生成Collector实例

Collectors常用API

静态方法 描述
Collector toList() 将输入的元素,收集到一个List容器中
Collector toSet() 将输入的元素,收集到一个Set容器中
Collector> toMap(
Function keyMapper,
Function valueMapper)
第一个参数keyMapper:获取map的key的Function
第二个参数valueMapper:获取map的value的Function
将输入的元素,收集到一个Map>容器中
Collector> toConcurrentMap(
Function keyMapper,
Function valueMapper)
第一个参数keyMapper:获取map的key的Function
第二个参数valueMapper:获取map的value的Function
将输入的元素,收集到一个ConcurrentMap容器
2.4.2 collect操作测试用例
public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamCollect() {
 Stream stream2List = persons.stream();
 // 将流中元素收集成 List
 List list = stream2List.collect(Collectors.toList());
 System.out.println("将流中元素收集成 List :n" + list);

 Stream stream2Map = persons.stream();
 // 将流中元素收集成 map,key: name,value: person
 // Function,通过输入的Person,获取name作为map的key
 Function nameFunc = item -> item.getName();
 // Function,通过输入的Person,获取 person 作为map的value
 Function personFunc = item -> item;
 Map map = stream2Map.collect(Collectors.toMap(nameFunc, personFunc));
 System.out.println("n将流中元素收集成 map,key: name,value: person :n" + map);
    }
}    

运行结果:

将流中元素收集成 List :
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

将流中元素收集成 map,key: name,value: person :
{孙七=Person{name='孙七', age=16}, 李四=Person{name='李四', age=29}, 张三=Person{name='张三', age=24}, 刘一=Person{name='刘一', age=25}, 陈二=Person{name='陈二', age=12}, 王五=Person{name='王五', age=22}, 周八=Person{name='周八', age=18}, 赵六=Person{name='赵六', age=15}}

2.5 count

count操作:返回到达终止操作时,流中元素总数

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamCount() {
 Stream stream = persons.stream();
 // 流中元素总数
 long count = stream.count();
 System.out.println("流中元素总数: " + count);
    }
}    

运行结果:

流中元素总数: 8
2.6 findAny

findAny 操作:返回任意一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamFindAny() {
 Stream stream = persons.stream();
 // 非空流:返回任意一个元素
 Optional any = stream.findAny();
 System.out.println("非空流:返回任意一个元素: " + any);

 Stream emptyStream = Stream.empty();
 // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
 Optional findAnyEmpty = emptyStream.findAny();
 System.out.println("空流:取任意一个元素时返回空的Optional: " + findAnyEmpty);
    }
}    

运行结果:

非空流:返回任意一个元素: Optional[Person{name='刘一', age=25}]
空流:取任意一个元素时返回空的Optional: Optional.empty
2.7 findFirst

findFirst操作:返回流中第一个元素,用Optional描述,如果是空流,返回空的Optional(Optional.empty),Optional的value值为null。

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamFindFirst() {
 Stream stream = persons.stream();
 // 非空流:返回流中第一个元素
 Optional first = stream.findFirst();
 System.out.println("非空流:返回流中第一个元素: " + first);

 Stream emptyStream = Stream.empty();
 // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
 Optional findFirstEmpty = emptyStream.findFirst();
 System.out.println("空流:取第一个第一个元素时返回空的Optional: " + findFirstEmpty);
    }
}    

运行结果:

非空流:返回流中第一个元素: Optional[Person{name='刘一', age=25}]
空流:取第一个第一个元素时返回空的Optional: Optional.empty
2.8 forEach

forEach操作:对此流的每个元素执行 Consumer 操作。

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamForEach() {
 Stream stream = persons.stream();
 //forEach操作:对此流的每个元素执行 Consumer 操作
 // 打印流中的每一个元素
 System.out.println("打印流中的每一个元素:");
 stream.forEach(item -> System.out.println(item));

 Stream stream2 = persons.stream();
 // 把流中的每一个元素收集进list
 List list = new ArrayList<>();
 stream2.forEach(list::add);
 System.out.println("n把流中的每一个元素收集进list:");
 System.out.println(list);
    }
}    

运行结果:

打印流中的每一个元素:
Person{name='刘一', age=25}
Person{name='陈二', age=12}
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}

把流中的每一个元素收集进list:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

2.9 max

max 操作:根据给定的Comparator,返回流中最大的元素,用Optional描述,
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamMax() {
 Stream stream = persons.stream();

 // 返回流中年龄最大的元素
 // Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
 // stream.max(Comparator.comparingInt(person -> person.getAge()));
 // person的age属性比较器
 Comparator ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
 Optional max = stream.max(ageComparator);
 System.out.println("返回流中最大的元素:" + max);

 Stream emptyStream = Stream.empty();
 // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
 Optional maxInEmpty = emptyStream.max(ageComparator);
 System.out.println("空流:取最大元素时返回空的Optional: " + maxInEmpty);
    }
}    

运行结果:

返回流中最大的元素:Optional[Person{name='李四', age=29}]
空流:取最大元素时返回空的Optional: Optional.empty
2.10 min

min 操作:根据给定的Comparator,返回流中最小的元素,用Optional描述
如果是空流,返回空的Optional(Optional.empty),Optional的value值为null

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testStreamMin() {
 Stream stream = persons.stream();

 // 返回流中年龄最小的元素
 // Comparator.comparingInt(person -> person.getAge()) 等同于 (p1, p2) -> p1.getAge() - p2.getAge()
 // stream.min(Comparator.comparingInt(person -> person.getAge()));
 // person的age属性比较器
 Comparator ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();
 Optional min = stream.min(ageComparator);
 System.out.println("返回流中最小的元素:" + min);

 Stream emptyStream = Stream.empty();
 // 如果是空流,返回空的Optional(Optional.empty),Optional的value值为null
 Optional minInEmpty = emptyStream.min(ageComparator);
 System.out.println("空流:去最小元素时返回空的Optional: " + minInEmpty);
    }
}    

运行结果:

返回流中最小的元素:Optional[Person{name='陈二', age=12}]
空流:去最小元素时返回空的Optional: Optional.empty
2.11 Optional reduce(BinaryOperator accumulator)

Optional reduce(BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数为流中的第一个元素,第二个参数为流中元素的第二个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第三个元素;
依次类推。最终得到用Optional描述的T类型的结果。

    
    @Test
    public void testStreamReduce1() {
 // 计算 nums 数组中所有元素的和

 int[] nums = new int[]{1, 5, 2, 4, 8, 9};
 // Arrays.stream(T[] array) 将指定的数组作为数据源,返回一个顺序流
 IntStream stream = Arrays.stream(nums);
 // (n1, n2) -> n1 + n2 二元运算 lambda 表达式,用于返回两个数值 n1 + n2 的结果
 OptionalInt reduce = stream.reduce((n1, n2) -> n1 + n2);
 System.out.println("计算 nums 数组中素有元素的和: " + reduce.getAsInt());
    }

运行结果:

计算 nums 数组中素有元素的和: 29
2.12 T reduce(T identity, BinaryOperator accumulator)

T reduce(T identity, BinaryOperator accumulator):
第一次执行时,accumulator函数的第一个参数identity,第二个参数为流中元素的第一个元素;
第二次执行时,第一个参数为第一次函数执行的结果,第二个参数为流中的第二个元素;
依次类推。最终得到T类型的结果。

    
    @Test
    public void testStreamReduce2() {
 // strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀

 String[] strs = new String[]{"hello", "Stream", "终止操作", "reduce"};
 // Arrays.stream(strs) 将指定的数组作为数据源,返回一个顺序流
 Stream stream = Arrays.stream(strs);
 // (n1, n2) -> n1 + n2 二元运算 lambda 表达式,把 str1 和 str2 两个字符串用 " " 空格连接
 BinaryOperator binaryOperator = (str1, str2) -> {
     StringBuilder sb = new StringBuilder();
     sb.append(str1).append(" ").append(str2);
     return sb.toString();
 };
 String reduce = stream.reduce("黑桃", binaryOperator);
 System.out.println("strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀:n " + reduce);
    }

运行结果:

strs 数组中所有元素用空格连接,并加上 "黑桃" 字符串作为前缀:
 黑桃 hello Stream 终止操作 reduce
2.13 toArray

Object[] toArray() 操作:返回包含此流元素的数组.

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testToArray() {
 Stream stream = persons.stream();
 // 返回包含此流元素的数组
 Object[] objects = stream.toArray();
 System.out.println(Arrays.asList(objects));
    }
}    

运行结果:

[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

3 中间操作

中间操作可以像数据库操作一样,让我们可以对数据源进行一些聚合操作,比如 distinct、limit…

**再次提示:**Stream 操作可以有 0个或者多个中间操作,但是 有且只有一个终止操作

这里我们先演示每个中间操作的单独使用,最后再演示多个中间操作串联的测试用例。

3.1 distinct

distinct 操作:返回由不同元素组成的流,根据元素的 equals() 方法去重

public class Person {

    //姓名
    private String name;
    //年龄
    private int age;

    public Person(String name, int age) {
 this.name = name;
 this.age = age;
    }

    public String getName() {
 return name;
    }

    public void setName(String name) {
 this.name = name;
    }

    public int getAge() {
 return age;
    }

    public void setAge(int age) {
 this.age = age;
    }

    @Override
    public String toString() {
 return "Person{" +
  "name='" + name + ''' +
  ", age=" + age +
  '}';
    }

    @Override
    public boolean equals(Object o) {
 if (this == o) return true;
 if (!(o instanceof Person)) return false;
 Person person = (Person) o;
 return getAge() == person.getAge() &&
  Objects.equals(getName(), person.getName());
    }

    @Override
    public int hashCode() {

 return Objects.hash(getName(), getAge());
    }

}

    
    @Test
    public void testDistinct() {
 Person[] personArray = new Person[]{
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("张三", 24),
  new Person("王五", 22),
  new Person("赵六", 15)
 };
 Stream stream = Arrays.stream(personArray);
 System.out.println("根据元素的equals()方法去重:");
 stream.distinct().forEach(item -> System.out.println(item));
    }

运行结果:

根据元素的equals()方法去重:
Person{name='张三', age=24}
Person{name='李四', age=29}
Person{name='王五', age=22}
Person{name='赵六', age=15}
3.2 filter

filter 操作:过滤,排除掉没有匹配 predicate 函数式接口的元素

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testFilter() {
 //过滤,返回年龄大于25岁的Person
 System.out.println("返回年龄大于25岁的Person:");
 // Predicate 函数式接口的 lambda表达式,判断传入的 person 的年龄是否大于25岁
 Predicate ageGt25Predicate = person -> person.getAge() > 25;
 persons.stream()
  .filter(ageGt25Predicate)
  .forEach(item -> System.out.println(item));
    }
}    

运行结果:

返回年龄大于25岁的Person:
Person{name='李四', age=29}
3.3 limit

limit 操作:限制流的元素数量,截断流,使其元素不超过给定数量

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testlimit() {
 System.out.println("persons 列表:n" + persons);

 // 返回 persons 列表中前3个元素
 List collect = persons.stream()
  .limit(3)//限制流中的最大元素数量为3
  .collect(Collectors.toList());//将流中元素收集到list中
 System.out.println("n返回 persons 列表中前3个元素:n" + collect);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

返回 persons 列表中前3个元素:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}]

3.4 skip

skip 操作:跳过元素,返回一个扔掉了前 n 个元素的流。如果此流包含的元素少于n,则返回空流

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testSkip() {
 System.out.println("persons 列表:n" + persons);

 // 跳过 persons 列表中前3个元素
 List collect = persons.stream()
  .skip(3)//跳过流中的前3个元素
  .collect(Collectors.toList());//将流中元素收集到list中
 System.out.println("n跳过 persons 列表中前3个元素:n" + collect);

 // 如果此流包含的元素少于n,则返回空流
 List empty = persons.stream()
  //跳过流中的前10个元素,流包含的元素少于10,返回空流
  .skip(10)
  .collect(Collectors.toList());//将流中元素收集到list中
 System.out.println("n跳过 persons 列表中前10个元素:n" + empty);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

跳过 persons 列表中前3个元素:
[Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

跳过 persons 列表中前10个元素:
[]


3.5 sorted()

sorted 操作:自然排序后返回一个新的流,如果元素不是Comparable类型,则抛出ClassCastException


    
    @Test
    public void testSorted() {
 int[] nums = new int[]{1, 5, 2, 4, 8, 9};
 System.out.println("对数组进行自然排序:");
 Arrays.stream(nums).sorted().forEach(System.out::println);
    }

运行结果:

对数组进行自然排序:
1
2
4
5
8
9
3.6 Stream sorted(Comparator comparator)

Stream sorted(Comparator comparator) 操作:根据给定 Comparator 排序后,返回一个新流。

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testSorted2() {
 System.out.println("persons 列表:n" + persons);
 // person的age属性比较器
 Comparator ageComparator = (p1, p2) -> p1.getAge() - p2.getAge();

 System.out.println("按照 age 升序排序:");
 persons.stream()
  // 根据给定 Comparator 排序
  .sorted(ageComparator)
  .forEach(System.out::println);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
按照 age 升序排序:
Person{name='陈二', age=12}
Person{name='赵六', age=15}
Person{name='孙七', age=16}
Person{name='周八', age=18}
Person{name='王五', age=22}
Person{name='张三', age=24}
Person{name='刘一', age=25}
Person{name='李四', age=29}
3.7 map

map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testMap() {
 System.out.println("persons 列表:n" + persons);

 //获取 persons 中每个元素的 name 属性存放到  List 中
 List list = persons.stream()
  //map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
  // Function函数式接口,获取入参person中的 name 属性
  // 将 Person 类型元素映射成String类型的新元素
  .map(person -> person.getName())
  // 将流中元素收集成 List
  .collect(Collectors.toList());
 System.out.println("获取 persons 中每个元素的 name 属性存放到  List 中:n" + list);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 name 属性存放到  List 中:
[刘一, 陈二, 张三, 李四, 王五, 赵六, 孙七, 周八]
3.8 mapToInt、mapToLong、mapToDouble

mapToInt 操作:通过入参ToIntFunction函数式接口将流中的每个T类型元素映射成一个Integer类型的新元素
mapToLong 操作:通过入参ToLongFunction函数式接口将流中的每个T类型元素映射成一个Long类型的新元素
mapToDouble 操作:通过入参ToDoubleFunction函数式接口将流中的每个T类型元素映射成一个Double类型的新元素

mapToInt简单使用
public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testMapToInt() {
 System.out.println("persons 列表:n" + persons);

 //获取 persons 中每个元素的 age 属性存放到  List 中
 List list = new ArrayList<>();
 persons.stream()
  //map 操作:通过入参Function函数式接口将流中的每个T类型元素映射成一个R类型的新元素
  // Function函数式接口,获取入参person中的 age 属性
  // 将 Person 类型元素映射成 int 类型的新元素
  .mapToInt(person -> person.getAge())
  // 将流中元素收集成 List
  .forEach(list::add);
 System.out.println("获取 persons 中每个元素的 age 属性存放到  List 中:n" + list);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]
获取 persons 中每个元素的 age 属性存放到  List 中:
[25, 12, 24, 29, 22, 15, 16, 18]

3.9 peek

peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作,如给元素属性赋值,每个元素引用没变

public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testPeek() {
 System.out.println("persons 列表:n" + persons);

 System.out.println("n给 persons 中每个元素的 name 属性添加"_peek"后缀:");
 //给 persons 中每个元素的 name 属性添加"_peek"后缀
 persons.stream()
  //peek 操作:通过入参Consumer函数式接口将流中的每个T类型元素进行操作
  // Consumer函数式接口,将入参person中的 name 属性添加"_peek"后缀
  // 将 Person 类型元素映射成String类型的新元素
  .peek(person -> {
      String name = person.getName();
      person.setName(name + "_peek");
  })
  .forEach(System.out::println);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

给 persons 中每个元素的 name 属性添加"_peek"后缀:
Person{name='刘一_peek', age=25}
Person{name='陈二_peek', age=12}
Person{name='张三_peek', age=24}
Person{name='李四_peek', age=29}
Person{name='王五_peek', age=22}
Person{name='赵六_peek', age=15}
Person{name='孙七_peek', age=16}
Person{name='周八_peek', age=18}

3.10 flatMap

flatMap 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个流,然后把转换后的所有流连接成一个流

    
    @Test
    public void testFlatMap() {
 Person[] array1 = new Person[]{
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
 };
 Person[] array2 = new Person[]{
  new Person("刘一", 25),
  new Person("陈二", 12),
 };
 Person[] array3 = new Person[]{
  new Person("孙七", 16),
  new Person("周八", 18)
 };
 // 将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List 中
 List list = Stream.of(array1, array2, array3)
  //flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
  // 然后把转换后的所有流连接成一个流
  .flatMap(array -> Arrays.stream(array))
  // 将流中元素收集成 List
  .collect(Collectors.toList());
 System.out.println("将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List 中:n" + list);
    }   

运行结果:

将 array1、array2、array3 3个person数组的所有Person元素收集存放到  List 中:
[Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

3.11 flatMapToInt、flatMapToLong、flatMapToDouble

flatMapToIn 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个IntStream(流中元素的类型是Integer),然后把转换后的所有IntStream连接成一个IntStream
flatMapToLong 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个LongStream(流中元素的类型是Long),然后把转换后的所有LongStream连接成一个LongStream
flatMapToDouble 操作:通过入参Function函数式接口将流中的每个元素都换成单独一个DoubleStream(流中元素的类型是Double),然后把转换后的所有DoubleStream连接成一个DoubleStream

flatMapToInt 简单使用
    
    @Test
    public void testFlatMapToInt() {
 Person[] array1 = new Person[]{
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
 };
 Person[] array2 = new Person[]{
  new Person("刘一", 25),
  new Person("陈二", 12),
 };
 Person[] array3 = new Person[]{
  new Person("孙七", 16),
  new Person("周八", 18)
 };
 // 将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List 中:
 List list = new ArrayList<>();
 Stream.of(array1, array2, array3)
  //flatMap 操作:通过入参Function函数式接口将流中的每个元素(Person[])都换成单独一个流,
  // 然后把转换后的所有流连接成一个流
  .flatMapToInt(array -> {
      int[] ages = new int[array.length];
      for (int i = 0; i < array.length; i++) {
   ages[i] = array[i].getAge();
      }
      return IntStream.of(ages);
  })
  // 将流中元素收集成 List
  .forEach(item -> list.add(item));
 System.out.println("将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List 中:n" + list);
    }

运行结果:

将 array1、array2、array3 3个person数组的所有Person的age收集存放到 List 中:
[24, 29, 22, 25, 12, 16, 18]
3.12 多个中间操作连接
public class StreamAPITest {
    List persons = null;

    @Before
    public void before() {
 persons = Arrays.asList(
  new Person("刘一", 25),
  new Person("陈二", 12),
  new Person("张三", 24),
  new Person("李四", 29),
  new Person("王五", 22),
  new Person("赵六", 15),
  new Person("孙七", 16),
  new Person("周八", 18)
 );
    }
    
    
    @Test
    public void testMultiOpr() {
 System.out.println("persons 列表:n" + persons);

 //将 persons 中 age 大于18 的前两个person的name 收集到  List 中
 List collect = persons.stream()
  //过滤出年龄大于18的元素
  .filter(person -> person.getAge() > 18)
  //限制返回前两个元素
  .limit(2)
  // 映射:返回person的name属性
  .map(person -> person.getName())
  // 将流中的元素收集到 list 中
  .collect(Collectors.toList());
 System.out.println("n将 persons 中 age 大于18 的前两个person的name 收集到  List 中:");
 System.out.println(collect);
    }
}    

运行结果:

persons 列表:
[Person{name='刘一', age=25}, Person{name='陈二', age=12}, Person{name='张三', age=24}, Person{name='李四', age=29}, Person{name='王五', age=22}, Person{name='赵六', age=15}, Person{name='孙七', age=16}, Person{name='周八', age=18}]

将 persons 中 age 大于18 的前两个person的name 收集到  List 中:
[刘一, 张三]

Stream 提供了大量的对集合等数据源的增强操作,上面给大家演示的是工作中比较常用的创建中间操作终止操作的API使用。
Stream+Lambda表达式 让我们对集合等数据源的操作更加简洁清晰,便于代码的维护。希望大家学以致用,熟能生巧。

代码:
https://github.com/wengxingxia/001java8.git

转载请注明:文章转载自 www.mshxw.com
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号