H4_Lambda衍生_Stream流

引入

Lambda的衍生物Stream
  1. 回顾
    几乎所有的集合(如 Collection 接口或 Map 接口等)都支持直接或间接的遍历操作。而当我们需要对集合中的元素进行操作的时候,除了必需的添加、删除、获取外,最典型的就是集合遍历。

传统做法:

public class D01List {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("张无忌");
		list.add("周芷若");
		list.add("赵敏");
		list.add("张强");
		list.add("张三丰");
		List<String> listA = new ArrayList<>();
		for (String name : list) {
			if (name.startsWith("张")) {
				listA.add(name);
			}
		}
		List<String> listB = new ArrayList<>();
		for (String name : listA) {
			if (name.length()==3) {
				listB.add(name);
			}
		}
		for (String s : listB) {
			System.out.println(s);
		}
	}
}
  1. Java 8的Lambda让我们可以更加专注于做什么(What),而不是怎么做(How),我们使用循环是为了遍历,对元素逐一处理,但循环只是一种方式,我么应该关注的在怎么处理(过滤)

改进:使用Stream流进行过滤输出

/*
 	使用Stream流的方式,遍历集合,对集合中的数据进行过滤
 	Stream是jdk1.8之后出现的,它关注的是做什么而不是怎么做
 	
 	Stream<T> filter​(Predicate<? super T> predicate) 返回由与此给定谓词匹配的此流的元素组成的流
 */
public class D02Stream {
	public static void main(String[] args) {
		List<String> list = new ArrayList<>();
		list.add("张无忌");
		list.add("周芷若");
		list.add("赵敏");
		list.add("张强");
		list.add("张三丰");
		//对list集合中的元素进行过滤,只要以"张"开头,存储到一个新的集合中去
		//对list集合进行过滤,只要名字长度为3的人,存储到一个新的集合中去
		//遍历list集合
		list.stream() 	//两次过滤,一次输出
					.filter(name->name.startsWith("张"))
					.filter(name->name.length()==3)
					.forEach(name -> System.out.println(name));
	}
}

流式思想

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

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组 等。
    (补充:“Stream流”其实是一个集合元素的函数模型,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其地址值))
  1. 和以前的Collection操作不同, Stream操作还有两个基础的特征:
  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluentstyle)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者增强for的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式,流可以直接调用遍历方法。
  1. 当使用一个流的时候,通常包括三个基本步骤:
    获取一个数据源(source)→ 数据转换→执行操作获取想要的结果,每次转换原有 Stream 对象不改变,返回一个新的 Stream 对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道

  2. 获取流

  • java.util.stream.Stream 是Java 8新加入的最常用的流接口。(这并不是一个函数式接口。)
  • 获取一个流非常简单,有以下几种常用的方式:
    • 所有的 Collection 集合 都可以通过 stream 默认方法获取流;
      default Stream stream​() 返回一个序列 Stream与此集合作为其来源
    • Stream 接口的静态方法 of 可以获取数组 对应的流。
      static Stream of​(T… values) 返回其元素是指定值的顺序排序流
      参数是一个可变参数,那么我们就可以传递一个数组
public class D03GetStream {
	public static void main(String[] args) {
		//把集合转换为Stream流(注意:必须是单列集合): Collection 集合通过 stream 默认方法获取流
		
		List<String> list = new ArrayList<String>();
		Stream<String> stream = list.stream();
		
		Set<String> set = new HashSet<String>();
		Stream<String> stream2 = set.stream();
		
		
		//map集合需要转换
		//获取键存储到一个set集合中
		Map<String, String> map = new HashMap<String, String>();
		Set<String> keyset = map.keySet();
		Stream<String> stream3 = keyset.stream();
		//获取值,存储到一个collection集合中
		Collection<String> values = map.values();
		Stream<String> stream4 = values.stream();
		//获取键值对(键与值的映射)
		Set<Entry<String, String>> entrySet = map.entrySet();
		Stream<Entry<String, String>> stream5 = entrySet.stream();
		
		
		//把数组转换为Stream流:Stream 接口的静态方法 of 可以获取数组对应的流。
		Stream<Integer> stream6 = Stream.of(1,2,3,4,5);
		//参数是一个可变参数,那么我们就可以传递一个数组
		Integer[] arr = {1,2,3,4,5};
		Stream<Integer> stream7 = Stream.of(arr);
		String[] arr2 = {"a","bb","ccc"};
		Stream<String> stream8 = Stream.of(arr2);
	}
}

Stream流的常用方法
  • 延迟方法:返回值类型仍然是 Stream 接口自身类型的方法,因此支持链式调用。(除了终结方法外,其余方法均为延迟方法。)
  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法,因此不再支持类似 StringBuilder 那样的链式调用。(count \ forEach)
  • Stream是一个管道流,只能被消费(使用)一次
  1. forEach(终结)
    void forEach(Consumer<? super T> action)
    该方法接收一个 Consumer 接口函数,会将每一个流元素交给该函数进行处理。
    Consumer接口是一个消费型接口,可以传递Lambda表达式,消费数据。
  • 简单记:
    forEach方法,用来遍历流中的数据,是一个终结方法,遍历之后不能再继续调用Stream流中的其他方法
public class D04ForEach {
	public static void main(String[] args) {
		//获取Stream流
		Stream<String> stream = Stream.of("张三","李四","王五","马六");
		//调用tream流中的forEach对Stream流中的数据进行遍历
		/*
		stream.forEach((String name)->{
			System.out.println(name);
		});
		*/
		
		//优化
		stream.forEach(name->System.out.println(name));
	}
}
  1. filter
    Stream filter(Predicate<? super T> predicate);
    该接口接收一个 Predicate 函数式接口参数(可以是一个Lambda或方法引用)作为筛选条件
  • Predicate:boolean test(T t)
    该方法将会产生一个boolean值结果,代表指定的条件是否满足。
    - 如果结果为true,那么Stream流的 filter 方法将会留用元素;
    - 如果结果为false,那么 filter 方法将会舍弃元素
public class D05Filter {
	public static void main(String[] args) {
		Stream<String> stream = Stream.of("赵敏","张翠山","张三丰","周芷若","张无忌");
		Stream<String> stream2 = stream.filter((String name)->{return name.startsWith("张");});  //筛选出新的流
		stream2.forEach(name->System.out.println(name)); //张翠山,张三丰,张无忌
		/*
		 	Stream是一个管道流,只能被消费(使用)一次
		 	第一个Stream流调用完方法,数据就会流转到下一个Stream流
		 	而这时第一个Stream流已经使用完毕,就会关闭了
		 	所以第一个Stream就不能再调用方法了
		 */
//		stream.forEach(name->System.out.println(name)); stream has already been operated upon or closed
	}
}
  1. map
    如果需要将流中的元素映射到另一个流中,可以使用 map 方法
  • Stream map(Function<? super T, ? extends R> mapper);
    该接口需要一个 Function 函数式接口参数,可以将当前流中的T类型数据转换为另一种R类型的流。

    java.util.stream.Function 函数式接口,其中唯一的抽象方法为:R apply(T t);
    这可以将一种T类型转换成为R类型,而这种 转换的动作,就称为“映射”

public class D06Function {
	public static void main(String[] args) {
		//获取一个String类型的Stream流
		Stream<String> stream = Stream.of("1","2","3","4");
		Stream<Integer> stream2 = stream.map((String s)->{
			return Integer.parseInt(s);
		});
		stream2.forEach(in->System.out.println(in+1));  //2 3 4 5
	}
}
  1. count(终结)
    正如旧集合 Collection 当中的 size 方法一样,流提供 count 方法来统计Stream流中元素的个数。
    long count():该方法返回一个long值代表元素个数(不再像旧集合那样是int值)

```java
public class D07Count {
	public static void main(String[] args) {
		ArrayList<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		Stream<Integer> stream = list.stream();
		long count = stream.count();
		System.out.println(count); //5
	}
}
  1. limit
  • limit 方法用于截取流中的元素,只取用前n个:Stream limit(long maxSize);
    参数是一个long型,如果集合当前长度大于参数则进行截取;否则不进行操作。
  • limit方法是一个延迟方法,只是对流中的元素进行截取,返回的是一个新的流,所以可以继续调用Stream流中的其他方法。
public class D08Limit {
	public static void main(String[] args) {
		String[] arr = {"美羊羊","喜羊羊","懒羊羊","灰太狼","红太狼"};
		//使用Stream流的limit方法对流中元素进行截取,只要前三个
		Stream<String> stream = Stream.of(arr).limit(3);
		stream.forEach(str->System.out.println(str)); //美羊羊、喜羊羊、懒羊羊
	}
}
  1. skip
    如果希望跳过前几个元素,可以使用 skip 方法获取一个截取之后的新流:Stream skip(long n);
    如果流的当前长度大于n,则跳过前n个;否则将会得到一个长度为0的空流。
public class D09Skip {
	public static void main(String[] args) {
		String[] arr = {"美羊羊","喜羊羊","懒羊羊","灰太狼","红太狼"};
		//使用Stream流的limit方法对流中元素进行截取,只要前三个
		Stream<String> stream = Stream.of(arr).skip(3);
		stream.forEach(str->System.out.println(str));  //灰太狼、红太狼
	}
}

需要截取前面的元素用limit,需要截取后面的元素则用skip(跳过)

  1. concat
    如果有两个流,希望合并成为一个流,那么可以使用 Stream 接口的静态方法 concat
    static Stream concat(Stream<? extends T> a, Stream<? extends T> b)
public class D10concat {
	public static void main(String[] args) {
		Stream<String> stream1 = Stream.of("赵敏","张翠山","张三丰","周芷若","张无忌");
		String[] arr = {"美羊羊","喜羊羊","懒羊羊","灰太狼","红太狼"};
		Stream<String> stream2 = Stream.of(arr);
		//把以上两个流合并成一个流
		Stream<String> concat = Stream.concat(stream1, stream2);
		concat.forEach(name->System.out.println(name)); //包含两个流中所有元素
	}
}
  1. 练习:集合元素处理
  • 传统方式:
/*
 	现在有两个 ArrayList 集合存储队伍当中的多个成员姓名,要求使用传统的for循环(或增强for循环)依次进行以
	下若干操作步骤:
	1. 第一个队伍只要名字为3个字的成员姓名;存储到一个新集合中。
	2. 第一个队伍筛选之后只要前3个人;存储到一个新集合中。
	3. 第二个队伍只要姓张的成员姓名;存储到一个新集合中。
	4. 第二个队伍筛选之后不要前2个人;存储到一个新集合中。
	5. 将两个队伍合并为一个队伍;存储到一个新集合中。
	6. 根据姓名创建 Person 对象;存储到一个新集合中。
	7. 打印整个队伍的Person对象信息。

 */
public class D11Test1 {
	public static void main(String[] args) {
		//第一支队伍
		ArrayList<String> one = new ArrayList<>();
		one.add("迪丽热巴");
		one.add("宋远桥");
		one.add("苏星河");
		one.add("石破天");
		one.add("石中玉");
		one.add("老子");
		one.add("庄子");
		one.add("洪七公");
		ArrayList<String> one1 = new ArrayList<String>();
		ArrayList<String> one2 = new ArrayList<String>();
		for (String name : one) {
			if(name.length()==3) {
				one1.add(name);
			}
		}
		for (int i = 0; i < 3; i++) {
			one2.add(one1.get(i));  //0 1 2
		}
		//第二支队伍
		ArrayList<String> two = new ArrayList<>();
		ArrayList<String> two1 = new ArrayList<>();
		ArrayList<String> two2 = new ArrayList<>();
		two.add("古力娜扎");
		two.add("张无忌");
		two.add("赵丽颖");
		two.add("张三丰");
		two.add("尼古拉斯赵四");
		two.add("张天爱");
		two.add("张二狗");
		for (String name : two) {
			if(name.startsWith("张")){
				two1.add(name);
			}
		}
		for (int i = 2; i < two1.size(); i++) {
			two2.add(two1.get(i)); //i 不包含0 1 
		}
		
		//5 将两个队伍合并为一个队伍;存储到一个新集合中。
		ArrayList<String> all = new ArrayList<String>();
		all.addAll(one2);
		all.addAll(two2);
		
		//6  根据姓名创建 Person 对象;存储到一个新集合中。
		ArrayList<Person> list = new ArrayList<>();
		for (String name : all) {
			list.add(new Person(name));
		}
		//打印整个队伍Person的对象信息
		for (Person person : list) {
			System.out.println(person);
		}
	}
}

  • 使用Stream流
public class D11Test2 {
	public static void main(String[] args) {
		//第一支队伍
		ArrayList<String> one = new ArrayList<>();
		one.add("迪丽热巴");
		one.add("宋远桥");
		one.add("苏星河");
		one.add("石破天");
		one.add("石中玉");
		one.add("老子");
		one.add("庄子");
		one.add("洪七公");
		Stream<String> oneStream = one.stream().filter(name->name.length()==3).limit(3);
		
		//第二支队伍
		ArrayList<String> two = new ArrayList<>();
		two.add("古力娜扎");
		two.add("张无忌");
		two.add("赵丽颖");
		two.add("张三丰");
		two.add("尼古拉斯赵四");
		two.add("张天爱");
		two.add("张二狗");
		Stream<String> twoStream = two.stream().filter(name -> name.startsWith("张")).skip(2);
		
		//合并流
		Stream.concat(oneStream, twoStream).map(name -> new Person(name)).forEach(p -> System.out.println(p));
	}	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值