jdk1.8新特性——Stream流式编程

Stream流是Java 8新加入的最常用的流接口,位于java.util.stream包下(java.util.stream.Stream ),这并不是一个函数式接口,接口里面包含了很多抽象方法,这些方法对于Stream流的操作都是至关重要的,最常见的方法有filter、map、foreach、count、limit、skip、concat

获取Stream流的两种方法

  1. 所有的 Collection 集合都可以通过 stream 默认方法获取流,stream()方法是Collection集合接口的一个默认方法,需要注意的是Map集合接口并不是Collection接口的子接口,所以不能直接调用stream方法获取Stream流,但是却可以分别对key和value获取Stream流;
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
Stream<Integer> stream = list.stream();
Set<String> set=new HashSet<String>();
set.add("aa");
set.add("bb");
set.add("cc");
Stream<String> stream = set.stream();
Map<String, String> map=new HashMap<String, String>();
map.put("name", "xiaoming");
map.put("sex", "man");
Stream<String> stream1 = map.keySet().stream();
Stream<String> stream2 = map.values().stream();
  1. Stream 接口的静态方法 of 可以获取数组对应的流,of 方法的参数其实是一个可变参数,所以支持数组。
Stream<Integer> stream5 = Stream.of(1,2,3);
String[] array={"aa","bb","cc"};
Stream<String> stream6 = Stream.of(array);

使用流式编程实现根据map集合的values进行过滤

Map<String, Integer> map = new HashMap<>();
map.put("A", 10);
map.put("B", 20);
map.put("C", 30);
Map<String, Integer> result = map.entrySet().stream().filter(entry -> entry.getValue() > 20).collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()));

Stream流常用方法

对于Stream流的常用方法,可以分成延迟方法和终结方法

  1. 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,支持链式调用(除了终结方法外,其余方法均为延迟方法)
  2. 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持链式调用。常见的终结方法有 count 和forEach 方法
1. foreach:遍历(对流中的元素进行遍历)

void forEach(Consumer<? super T> action); 该方法接收一个Consumer函数式接口,方法返回值是void,并非Stream接口,所以属于终结方法;

2. filter:过滤(对流进行一个过滤操作,只保留满足条件的元素)

Stream<T> filter(Predicate<? super T> predicate);该方法接收一个Predicate函数式接口,方法返回值是Stream接口,还可以进行链式调用,所以属于延迟方法;Predicate接口中的唯一抽象方法——test方法将会产生一个boolean值结果,代表指定的条件是否满足。如果结果为true,那么Stream流的 filter 方法将会留用元素;如果结果为false,那么 filter 方法将会舍弃元素;

String[] array = {"xiaoming","xiao","hong"};
Stream<String> stream1 = Stream.of(array);
Stream<String> stream2 = stream1.filter(s->s.length()>4);//过滤出长度大于4的字符串
stream2.forEach(s->System.out.println(s));//xiaoming
3. map:映射(将流中的元素映射到另一个流中)

<R> Stream<R> map(Function<? super T, ? extends R> mapper); 该方法接收一个Function函数式接口,方法返回值是Stream接口,还可以进行链式调用,所以属于延迟方法;Function接口中的唯一抽象方法——apply方法接收一个参数并返回一个参数,也就是可以将一种T类型转换成为R类型,而这种转换的动作,就称为“映射”;

Stream<Integer> stream1 = Stream.of(1,2,3);
//将原来流中int类型元素映射为String类型
Stream<String> stream2 = stream1.map(i->String.valueOf(i));
stream2.forEach(s->System.out.println(s));
4. count:统计(计算流中元素个数)

long count(); 该方法返回值是long不是int,这一点需要注意,另外方法返回值是long,并非Stream接口,所以属于终结方法;

Stream<Integer> stream = Stream.of(1,2,3);
long count = stream.count();
System.out.println(count);//3
5. limit:(截取流中前几个元素)

Stream<T> limit(long maxSize); 如果集合当前长度大于参数则进行截取;否则不进行操作,也就是返回原来的流对象,并不会报错

Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
Stream<Integer> stream2 = stream1.limit(3);
stream2.forEach(s->System.out.println(s));//1 2 3
Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
Stream<Integer> stream2 = stream1.limit(10);
stream2.forEach(s->System.out.println(s));//1 2 3 4 5
6. skip:(跳过流中前几个元素)

Stream<T> skip(long n); 如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流;

Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
Stream<Integer> stream2 = stream1.skip(3);
stream2.forEach(s->System.out.println(s));//4 5
Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
Stream<Integer> stream2 = stream1.skip(10);
stream2.forEach(s->System.out.println(s));//没有任何输出,也就是得到的是一个空流
7. concat:组合(组合两个流)

static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b)该方法是Stream接口中的一个静态方法。

Stream<Integer> stream1 = Stream.of(1,2);
Stream<Integer> stream2 = Stream.of(3,4);
Stream<Integer> stream3 = Stream.concat(stream1, stream2);
stream3.forEach(s->System.out.println(s));//1 2 3 4
8. collect:(将stream流转换成集合)
List<Person> l = list.stream().filter(person -> "xiao".equals(person.getName())).collect(Collectors.toList());
Set<Person> set = list.stream().filter(person -> "xiao".equals(person.getName())).collect(Collectors.toSet());

Stream流编程需要注意的点

Stream流属于管道流,只能被消费一次,也就是说第一个Stream流调用方法完毕之后,数据会流到下一个Stream流,并且该流会被关闭,再次使用该流去调用方法将会报错

public class Main {
	public static void main(String[] args) {
		Stream<Integer> stream = Stream.of(1,2,3,4,5);
		long count = stream.count();//第一次使用stream流
		stream.forEach(s->System.out.println(s));//第二次使用stream流
	}
}

在这里插入图片描述

使用Stream流编程的好处

先看下面两段代码:
代码一:

List<Report> reportList = reportDao.getReportData(queryReport);
//过滤掉pay_out=0的数据
Iterator<Report> iterator = reportList.iterator();
while (iterator.hasNext()){
    Report report = iterator.next();
    if (report.getPayOut().equals("0")) {
        iterator.remove();
    }
}

代码二:

List<Report> reportList = reportDao.getReportData(queryReport);
//过滤掉pay_out=0的数据
List<Report> filterByPayoutReportList = reportList.stream().filter(report ->
	!"0".equals(report.getPayOut())).collect(toList());

以上两段代码都是为了实现我的需求:先从数据库查询出符合条件的所有数据,并存在一个list集合中,但是我需要对这个list集合进行一个过滤,将pay_out=0的数据过滤掉;第一段代码使用的是传统的方法,遍历list集合,并将pay_out=0的元素remove掉,这种方法看起来没什么问题,但是后面我需要使用最原先从数据库查询出来的reportList的时候,就发现这个reportList已经被修改了,这就是使用传统方式带来的弊端,过滤的时候直接就将整个list集合的结构修改了;

而第二段代码使用的是jdk1.8的stream流式编程,调用stream流的filter方法进行过滤,过滤完之后再转换成一个新的list集合,从头到尾都没有修改最先的reportList集合,所以,我们可以在需要过滤掉pay_out=0的数据的地方使用filterByPayoutReportList集合,在其他地方还是使用reportList集合,这才是我们想要的效果。

由此可见,使用stream流式编程不仅可以简化代码开发,增强可读性,还可以在不修改原来集合的基础上对数据进行过滤、映射等操作。

关于jdk1.8的其它新特性:

  1. jdk1.8新特性——Lambda表达式与函数式接口:https://blog.csdn.net/can_chen/article/details/106886385
  2. jdk1.8新特性——Optional类:https://blog.csdn.net/can_chen/article/details/106886579
  3. jdk1.8新特性——方法引用:https://blog.csdn.net/can_chen/article/details/106886536
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值