Java流式编程详解

2年前 (2022) 程序员胖胖胖虎阿
224 0 0

文章目录

    • 1. 一个流的使用例子
    • 2. 流简介
    • 3. 流的特点
    • 4. 流基本操作
      • 4.1 中间操作
      • 4.2 终端操作
    • 5. 筛选和切片
      • 5.1 用谓词筛选
      • 5.2 筛选各异的元素
      • 5.3 截短流
      • 5.4 跳过元素
    • 6. 映射
      • 6.1 对流中每一个元素应用函数
      • 6.2 流的扁平化
    • 7. 查找和匹配
      • 7.1 检查谓词是否至少匹配一个元素
      • 7.2 检查谓词是否匹配所有元素
      • 7.3 查找任意元素
      • 7.4 查找第一个元素
    • 8. 归约
      • 8.1 元素求和
      • 8.2 最大值和最小值
    • 9. 数值流
      • 9.1 原始类型流特化
      • 9.2 数值范围
    • 10. 构建流
      • 10.1 由值创建流
      • 10.2 由数组创建流
      • 10.3 由文件生成流
      • 10.4 由函数生成流:创建无限流

1. 一个流的使用例子

场景:现在有一个菜品集合,需要找出菜品中的卡路里小于400的菜品并且按照卡路里多少进行排序

class Dish{
	String name;
	int price;
	int calories;
	
	public Dish(String name, int price, int calories) {
		super();
		this.name = name;
		this.price = price;
		this.calories = calories;
	}

	public String getName() {
		return name;
	}

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

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public int getCalories() {
		return calories;
	}

	public void setCalories(int calories) {
		this.calories = calories;
	}

	@Override
	public String toString() {
		return "Dish [name=" + name + ", price=" + price + ", calories=" + calories + "]";
	}
	
	
	
}

普通方式的实现:

public class StreamDemo {
	public static void main(String[] args) {
		List<Dish> menu=Arrays.asList(
				new Dish("pork",800),
				new Dish("beef",700),
				new Dish("chicken",400),
				new Dish("french fries",530),
				new Dish("rice",350),
				new Dish("fruit",120),
				new Dish("pizza",550));
		List<Dish> lowCaloriesDish=new ArrayList<>();
		for(Dish d:menu)
		{
			if(d.getCalories()<400)
				lowCaloriesDish.add(d);
		}
		Collections.sort(lowCaloriesDish,new Comparator<Dish>() {

			@Override
			public int compare(Dish o1, Dish o2) {
				return Integer.compare(o1.getCalories(), o2.getCalories());
			}
		});
		for(Dish d:lowCaloriesDish)
			System.out.println(d.getName()+"  "+d.getCalories());
	}
}

Java流式编程详解

使用流实现:

import java.util.List;
import static java.util.stream.Collectors.toList;
public class StreamDemo {
	public static void main(String[] args) {
		List<Dish> menu=Arrays.asList(
				new Dish("pork",800),
				new Dish("beef",700),
				new Dish("chicken",400),
				new Dish("french fries",530),
				new Dish("rice",350),
				new Dish("fruit",120),
				new Dish("pizza",550));
		List<String> lowCaloriesDishName=menu.stream()
				.filter(d->d.getCalories()<400)
				.sorted((d1,d2)->d1.getCalories()-d2.getCalories())
				.map(d->d.getName())
				.collect(toList());
		
		for(String name:lowCaloriesDishName)
			System.out.println(name);
	}
}

Java流式编程详解


2. 流简介

流是从支持数据处理操作的源生成的元素序列

  1. 元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值
  2. 源:流会使用一个提供数据的源,如集合、数组或输入/输出资源
  3. 数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等
  4. 流水线:很多流操作本身会返回一个流,这样多个操作就可以链接起来,形成一个大的流水线
  5. 内部迭代:与使用迭代器显式迭代的集合不同,流的迭代操作是在背后进行的

Java流式编程详解
Java流式编程详解
Java流式编程详解


3. 流的特点

1. 流只能遍历一次

public class StreamDemo {
	public static void main(String[] args) {
		List<String> title=Arrays.asList("Java","Python","Go");
		Stream<String> s=title.stream();
		s.forEach(System.out::println);
		s.forEach(System.out::println);
	}
}

Java流式编程详解

2. 流使用内部迭代

public class StreamDemo {
	public static void main(String[] args) {
		List<Dish> menu=Arrays.asList(
				new Dish("pork",800),
				new Dish("beef",700),
				new Dish("chicken",400),
				new Dish("french fries",530),
				new Dish("rice",350),
				new Dish("fruit",120),
				new Dish("pizza",550));
		/*
		 * 集合使用外部迭代
		 */
		List<String> names=new ArrayList<>();
		Iterator<Dish> iterator=menu.iterator();
		while(iterator.hasNext()) {
			Dish d=iterator.next();
			names.add(d.getName());
		}
		System.out.println(names);
		
		/*
		 * 流使用内部迭代
		 */
		List<String> names2=menu.stream()
				.map(Dish::getName)
				.collect(toList());
		System.out.println(names2);
		
	}
}


4. 流基本操作

4.1 中间操作

List<String> names=menu.stream()
				.filter(d->d.getCalories()>400)
				.map(d->d.getName())
				.limit(3)
				.collect(toList());
		System.out.println(names);
		

代码中的filter、map、limit是中间操作


4.2 终端操作

public static void main(String[] args) {
		List<Dish> menu=Arrays.asList(
				new Dish("pork",800),
				new Dish("beef",700),
				new Dish("chicken",400),
				new Dish("french fries",530),
				new Dish("rice",350),
				new Dish("fruit",120),
				new Dish("pizza",550));
		
		menu.stream().forEach(System.out::println);
		
	}

将流中的数据输出到终端上

Java流式编程详解
Java流式编程详解


5. 筛选和切片

5.1 用谓词筛选

filter:返回一个包括所有符合谓词的元素的流

public static void main(String[] args) {
		List<Dish> menu=Arrays.asList(
				new Dish("pork",800,false),
				new Dish("beef",700,false),
				new Dish("chicken",400,false),
				new Dish("french fries",530,false),
				new Dish("rice",350,false),
				new Dish("fruit",120,true),
				new Dish("tomato",200,true));
		
		List<Dish> vegetableMenu=menu.stream()
				.filter(d->d.isVegetable)//是否是蔬菜
				.collect(toList());
		System.out.println(vegetableMenu);
		
	}

5.2 筛选各异的元素

distinct():它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)的流

List<Integer> numbers=Arrays.asList(1,2,2,3,4,4,5,7);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.collect(toList());
		System.out.println(evenNumbers);//[2, 2, 4, 4]

上面代码在一个集合中寻找偶数,结果中有两个相同的2,两个相同的4

List<Integer> numbers=Arrays.asList(1,2,2,3,4,4,5,7);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.distinct()//distinct保证元素唯一
				.collect(toList());
		System.out.println(evenNumbers);//[2, 4]
		

5.3 截短流

limit(n):该方法会返回一个不超过给定长度的流

List<Integer> numbers=Arrays.asList(1,2,3,4,5,6,7,8,9);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.collect(toList());
		System.out.println(evenNumbers);//[2, 4, 6, 8]
List<Integer> numbers=Arrays.asList(1,2,3,4,5,6,7,8,9);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.limit(2)
				.collect(toList());
		System.out.println(evenNumbers);//[2, 4]

5.4 跳过元素

skip方法返回一个扔掉了前n个元素的流。如果流中元素不足n个,则返回一个空流

List<Integer> numbers=Arrays.asList(1,2,3,4,5,6,7,8,9);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.skip(1)//跳过流中的前n个元素
				.collect(toList());
		System.out.println(evenNumbers);//[4, 6, 8]

6. 映射

6.1 对流中每一个元素应用函数

		/*
		 * 先找出集合中的偶数  再将这些偶数进行平方操作
		 */
		List<Integer> numbers=Arrays.asList(1,2,3,4,5,6,7,8,9);
		List<Integer> evenNumbers=numbers.stream()
				.filter(x->x%2==0)//寻找偶数
				.map(x->x*x)
				.collect(toList());
		System.out.println(evenNumbers);//[2,4,6,8]->[4, 16, 36, 64]

6.2 流的扁平化

		List<String> words=Arrays.asList("Monday","Tuesday");
		List<String> characters=words.stream()
				.map(word->word.split(""))//将每个单词转化为一个字符串数组
				.flatMap(Arrays::stream)//将每个字符数组扁平化
				.collect(toList());
		System.out.println(characters);//[M, o, n, d, a, y, T, u, e, s, d, a, y]

Java流式编程详解


7. 查找和匹配

7.1 检查谓词是否至少匹配一个元素

anyMatch方法可以回答“流中是否有一个元素能匹配给定的谓词

List<Integer> numbers=Arrays.asList(1,2,4);
		if(numbers.stream().anyMatch(x->x%2==1)) {
			System.out.println("numbers集合中至少有1个奇数");
		}
		else {
			System.out.println("numbers集合中没有奇数");	
		}

7.2 检查谓词是否匹配所有元素

allMatch检查流中的元素是否都能匹配给定的谓词

List<Integer> numbers=Arrays.asList(1,2,3);
		if(numbers.stream().allMatch(x->x%2==1)) {
			System.out.println("numbers集合中都是奇数");
		}
		else {
			System.out.println("numbers集合中不全是奇数");	
		}

7.3 查找任意元素

findAny方法将返回当前流中的任意元素

List<Integer> numbers=Arrays.asList(1,2,3,4,5);
		Optional<Integer> allEven=numbers.stream()
				.filter(x->x%2==0)
				.findAny();
		System.out.println(allEven);

7.4 查找第一个元素

List<Integer> numbers=Arrays.asList(1,2,3,4,5);
		Optional<Integer> firstEven=numbers.stream()
				.filter(x->x%2==0)
				.findFirst();
		System.out.println(firstEven);//Optional[2]

8. 归约

8.1 元素求和

		/*
		 * reduce的第一个参数:初始值 相当于给求和一个初值
		 * reduce的第二个参数:一个BinaryOperator<T>来将两个元素结合起来产生一个新值
		 */
		List<Integer> numbers=Arrays.asList(4,3,5,9);
		int sum=numbers.stream().reduce(0, (a,b)->a+b);
		System.out.println("sum="+sum);//21

Java流式编程详解

8.2 最大值和最小值

		/*
		 * reduce重载的变体
		 * 它不接受初始值,但是会返回一个Optional对象
		 */
		List<Integer> numbers=Arrays.asList(4,3,5,9);
		Optional<Integer> maxNum=numbers.stream().reduce((x,y)->x>y?x:y);
		Optional<Integer> minNum=numbers.stream().reduce((x,y)->x<y?x:y);
		System.out.println("maxNum="+maxNum);//maxNum=Optional[9]
		System.out.println("minNum="+minNum);//minNum=Optional[3]

9. 数值流

9.1 原始类型流特化

class Goods{
	String name;
	int price;
	
	public String getName() {
		return name;
	}

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

	public int getPrice() {
		return price;
	}

	public void setPrice(int price) {
		this.price = price;
	}

	public Goods(String name, int price) {
		super();
		this.name = name;
		this.price = price;
	}

	@Override
	public String toString() {
		return "Goods [name=" + name + ", price=" + price + "]";
	}
	
}

1. 映射到数值流

mapToInt返回一个IntStream(而不是一个Stream)

public static void main(String[] args) {
		
		List<Goods> goods=Arrays.asList(
				new Goods("Apple", 10),
				new Goods("Banana", 5),
				new Goods("Bread", 3),
				new Goods("Milk", 15),
				new Goods("Wine", 20));
		//将Good对象流映射到价格流(整数流) 并求出价格总和
		int priceSum=goods.stream().mapToInt(g->g.getPrice()).sum();
		System.out.println(priceSum);//53
		
	}

2.转换回对象流

boxed方法:要把原始流转换成一般流(这里每个int都会装箱成一个Integer)

List<Goods> goods=Arrays.asList(
				new Goods("Apple", 10),
				new Goods("Banana", 5),
				new Goods("Bread", 3),
				new Goods("Milk", 15),
				new Goods("Wine", 20));
		IntStream intStream=goods.stream().mapToInt(g->g.getPrice());
		Stream<Integer> stream=intStream.boxed();

9.2 数值范围

Java 8引入了两个可以用于IntStream和LongStream的静态方法,帮助生成这种范围:range(左闭右开)和rangeClosed(左闭右闭)

//生成[1,50]内的所有偶数
		IntStream evenNums=IntStream.rangeClosed(1, 50).filter(x->x%2==0);
		evenNums.forEach(x->{System.out.print(" "+x);});

Java流式编程详解


10. 构建流

10.1 由值创建流

使用静态方法Stream.of,通过显式值创建一个流

		Stream<String> stream=Stream.of("Java","Python","Go");
		stream.forEach(System.out::println);

10.2 由数组创建流

使用静态方法Arrays.stream从数组创建一个流

		int[] numbers= {1,2,3,4,5,6};
		IntStream stream=Arrays.stream(numbers);
		stream.forEach(System.out::println);

10.3 由文件生成流

Stream<String> lines=null;
		try {
			lines=Files.lines(Paths.get("data.txt"),Charset.defaultCharset());	
			lines.forEach(System.out::println);
		} catch (IOException e) {
			
			e.printStackTrace();
		}finally {
			lines.close();
		}

Java流式编程详解

10.4 由函数生成流:创建无限流

1. 迭代

/*
		 * iterate方法接受一个初始值(在这里是0),还有一个依次应用在每个产生的新值上的Lambda(UnaryOperator<t>类型)
		 * 这里,我们使用Lambda n-> n+2,返回的是前一个元素加上2
		 */
		Stream.iterate(0,n->n+2)
		.limit(10)
		.forEach(System.out::println);
		
		/*
		 * 生成斐波那契数列
		 */
		Stream.iterate(new int[] {0,1},t->new int[] {t[1],t[0]+t[1]})
		.limit(10)
		.forEach(t->{System.out.println(t[0]+" "+t[1]);});

Java流式编程详解

2. 生成

		/*
		 * 生成10个随机数
		 */
		Stream.generate(Math::random)
		.limit(10)
		.forEach(System.out::println);

Java流式编程详解

版权声明:程序员胖胖胖虎阿 发表于 2022年10月9日 上午2:32。
转载请注明:Java流式编程详解 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...