Java8中Stream是对集合进行操作,可以执行非常复杂的查找、过滤、映射等操作。使用Stream API可以像SQL执行的数据库查询一样对集合进行操作。也可以进行并行操作。
Stream API提供了一种高效且易于实现的处理数据的方式。
集合讲的是数据,流讲的时计算!
**
Stream操作过程:
- 创建流
- (中间操作)处理流
- 终止操作
Stream特点:
- Stream不会存储元素
- Stream不会修改源数据,最终返回一个持有结果的新Stream
Stream操作时延迟的,这意味着它们会等到需要结果的时候才会执行。
创建Stream流
通过 Collection 系列集合提供的 stream() 方法或 parallelStream()方法创建流
- 通过Arrays 中静态方法 stream() 获取数组流
- 通过Stream 类的静态方法 of() 创建
- 创建无限流 Stream.iterate()
生产流 Stream.generate()
@Test
public void test05(){
// 方式一
Integer[] integers = new Integer[10];
Stream<Integer> stream1 = Arrays.stream(integers);
// 方式二
List<String> list = new ArrayList<>();
Stream<String> stream2 = list.stream();
// 方式三
Stream<String> integerStream = Stream.of("aa","bb","cc");
// 方式四
Stream<Integer> iterate = Stream.iterate(0, x -> x + 2);
// 方式五
Stream<Double> generate = Stream.generate(Math::random);
}
筛选与切片
filter 从流中排除某些元素
filter方法需要传入一个函数接口
Predicate
,通过Lambda表达式对流中每个数据进行判断,满足条件则添加到新的流中。Stream<T> filter(Predicate<? super T> predicate);
@Test
public void test06(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
// 创建流
Stream<Integer> stream = Arrays.stream(integers);
// 返回的是一个新的流,中间操作时不执行的
Stream<Integer> integerStream = stream.filter(integer -> integer % 2 == 0);
// 终止操作,输出流中内容
integerStream.forEach(integer -> System.out.print(integer+" "));
}
对于流的操作也可以写成
@Test
public void test06(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
Arrays.stream(integers).filter(integer -> integer%2==0).forEach(integer -> System.out.println(integer+" "));
}
limit 截断流,使其元素不超过给定数量
比如说,找到所需要的数据条数后,后续的操作就不再执行了,这个操作被称为(短路)
@Test
public void test06(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
Arrays.stream(integers)
.filter(integer -> integer%2==0)
.limit(2)
.forEach(integer -> System.out.print(integer+" "));
}
skip 跳过指定数量
@Test
public void test06(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10};
Arrays.stream(integers)
.filter(integer -> integer%2==0)
.skip(2)
.forEach(integer -> System.out.print(integer+" "));
}
distinct 去重
通过流所生成对象的hashCode() 和 equals() 去重元素
@Test
public void test06(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10,10,8,9};
Arrays.stream(integers)
.filter(integer -> integer%2==0)
.skip(2)
.distinct()
.forEach(integer -> System.out.print(integer+" "));
}
这里之所以可以去重,使用为 Integer类重写了hashCode() 和 equals() 方法。映射
map
接收Lambda(函数接口),将元素转换成其它形式或提取数据。接受一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素。
@Test
public void test07(){
List<String> list = Arrays.asList("aaa","bbb","ccc");
// 将流中每个元素转为大写
list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
}
嵌套流@Test
public void test08(){
List<Integer> list = Arrays.asList(123,456,789);
Stream<Stream<Integer>> streamStream = list.stream().map(integer -> filterCharacter(integer));
streamStream.forEach(s->s.forEach(System.out::println));
}
// 将数进行分解放入list中
public static Stream<Integer> filterCharacter(Integer integer){
List<Integer> list = new ArrayList<>();
while (integer%10 != 0){
list.add(integer%10);
integer = integer/10;
}
return list.stream();
}
flatMap
将流中每个元素转换成另一个流,然后把所有的流连接起来
@Test
public void test09(){
List<Integer> list = Arrays.asList(123,456,789);
list.stream().flatMap(integer -> filterCharacter(integer))
.forEach(i-> System.out.print(i+" "));
}
// 将数进行分解放入list中
public static Stream<Integer> filterCharacter(Integer integer){
List<Integer> list = new ArrayList<>();
while (integer%10 != 0){
list.add(integer%10);
integer = integer/10;
}
return list.stream();
}
排序
sorted() 自然排序
按字典排序,其内部时调用`compareTo() 方法进行排序
@Test
public void test10(){
List<String> list = Arrays.asList("ddd","bbb","ccc");
list.stream().sorted()
.forEach(System.out::println);
}
sorted(Comparator com) 定制排序
自己传入的排序方式进行排序
int compare(T o1, T o2);
该方法是根据放回来判断o1和o2的大小:
- 返回值小于0:o1<o2
- 返回值大于0:o1>o2
返回值等于0:o1=o2
@Test
public void test08(){
ArrayList<UserEntity> userEntities = new ArrayList<>();
userEntities.add(new UserEntity("小明",12));
userEntities.add(new UserEntity("小红",22));
userEntities.add(new UserEntity("小蓝",13));
userEntities.add(new UserEntity("小绿",16));
userEntities.add(new UserEntity("小紫",21));
userEntities.sort(new Comparator<UserEntity>() {
@Override
public int compare(UserEntity o1, UserEntity o2) {
return o1.getAge()- o2.getAge();
}
});
userEntities.forEach((u)-> System.out.println(u.toString()));
}
查找与匹配(终止操作)
- allMatch:所有都匹配则为true
- anyMatch:任意一个匹配则为true
- noneMatch:都没有匹配则为true
- findFirst:查找第一个元素
- findAny:查找任意一个元素
- count:返回元素数量
- max:范围自定义判断中最大的
min:范围自定义判断中最小的
@Test
public void test11(){
Integer[] integers = new Integer[]{1,2,3,4,5,6,7,8,9,10,10,8,9};
// 所有元素都匹配则为true
boolean b = Arrays.stream(integers).allMatch(x -> x % 2 == 0);
System.out.println(b);
// 任意一个匹配则为true
boolean b1 = Arrays.stream(integers).anyMatch(x -> x % 2 == 0);
System.out.println(b1);
// 都没有匹配则为true
boolean b2 = Arrays.stream(integers).noneMatch(x -> x % 2 == 0);
System.out.println(b2);
Optional<Integer> first = Arrays.stream(integers).findFirst();
System.out.println(first.get());
Optional<Integer> any = Arrays.stream(integers).findAny();
System.out.println(any.get());
long count = Arrays.stream(integers).count();
System.out.println(count);
Optional<Integer> max = Arrays.stream(integers).max((x, y) -> x.compareTo(y));
System.out.println(max.get());
Optional<Integer> min = Arrays.stream(integers).min((x, y) -> x.compareTo(y));
System.out.println(min.get());
}
归约与收集
reduce 归约
可以将流中元素反复结合起来,得到一个值
T reduce(T identity, BinaryOperator
accumulator); Optional
reduce(BinaryOperator accumulator);** @Test
public void test12(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(reduce);
Optional<Integer> reduce1 = list.stream().reduce((x, y) -> x + y);
System.out.println(reduce1.get());
}
第一个参数为起始值
**0**
,然后将**0**
赋值个x
,在将流中每一个元素赋值给y
,在执行方法体。最后算出累加和。而对于没有起始值的方法,它累加和是有可能为空,所以返回的是一个Optional类。collect 收集
将流转换为其它形式。接受一个Collector接口的实现类,用于给Stream中元素做魏总的方法。
Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List、Set、Map)。但Collectors实现类提供了很多静态方法,可以方法的创建常见收集器示例,具体方方法与示例如下表:
将Peson list中的姓名提取出来放入一个新的list中。
@Test
public void test13(){
List<Person> peoples = Arrays.asList(
new Person(1,"张三",29),
new Person(2,"李四",20),
new Person(3,"王五",25),
new Person(4,"小王",23),
new Person(5,"小陈",16)
);
List<String> collect = peoples.stream().map(p -> p.getName()).collect(Collectors.toList());
collect.forEach(System.out::println);
}
将List转为set,注意,set内部依赖于map去重,map依赖equals 和 hashCode 方法进行去重
@Test
public void test14(){
List<Person> peoples = Arrays.asList(
new Person(1,"张三",29),
new Person(2,"李四",20),
new Person(3,"王五",25),
new Person(4,"小王",23),
new Person(5,"小陈",16)
);
Set<Person> collect = peoples.stream().collect(Collectors.toSet());
collect.forEach(System.out::println);
}
如果People类中没有重写equals 和 hashCode 这两个方法,则去不了所谓的重复。
放入指定集合中
@Test
public void test15(){
List<Person> peoples = Arrays.asList(
new Person(1,"张三",29),
new Person(2,"李四",20),
new Person(3,"王五",25),
new Person(4,"小王",23),
new Person(5,"小陈",16)
);
Set<String> collect = peoples.stream().map(x->x.getName()).collect(Collectors.toCollection(HashSet::new));
collect.forEach(System.out::println);
}
分组,根据id分组,也可以进行多级分组,groupingBy中存在两个参数的方法
@Test
public void test16(){
List<Person> peoples = Arrays.asList(
new Person(1,"张三",29),
new Person(1,"李四",20),
new Person(2,"王五",25),
new Person(2,"小王",23),
new Person(3,"小陈",16)
);
Map<Integer, List<Person>> collect = peoples.stream().collect(Collectors.groupingBy(p -> p.getId()));
System.out.println(collect);
}
分区
@Test
public void test17(){
List<Person> peoples = Arrays.asList(
new Person(1,"张三",29),
new Person(1,"李四",20),
new Person(2,"王五",25),
new Person(2,"小王",23),
new Person(3,"小陈",16)
);
Map<Boolean, List<Person>> collect = peoples.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 25));
}