Java8实战1


title: Coding Qian
tag: 标签名
categories: 分类
comment: 是否允许评论(true or false)
description: 描述
top_img: https://z3.ax1x.com/2021/10/06/4xq2s1.png
cover:https://s4.ax1x.com/2022/02/11/HUIZbn.jpg

一 基础知识

为什么要关心Java8

Java在编程语言生态系统中的位置

Java式怎么进入编程市场的?

面向对象在20世纪90年代开始兴起的原因有两个:封装原则使得其软件工程问题比C少,作为一个思想模型,它轻松地反映了Windows95及之后的WIMP编程模式。Java的“一次编写,随处运行”模式,早期浏览器安全地执行Java小应用的能力让它占领了大学市场。

流处理

流是一系列数据项,一次只生成一项。程序可以从输入流中一个一个读取数据,然后以同样的方式将数据项写入输出流。一个程序的输出流很可能是另一个程序的输入流。

举一个例子:

在Unix中,命令(cat,tr,sort和tail)是同时执行的,这样sort就可以在cat和tr完成前先处理头几行。就像汽车组装流水线一样。汽车排队进入加工站,每个加工站会接收、修改汽车,然后将之传递给下一战做进一步的处理。尽管流水实际上是一个序列,但不同加工站的运行一般是并行的。

HYrUC6.png

基于上述思想,Java 8在java.util.Stream中添加了一个Stream API:Stream就是一系列T类型的项目。

用行为参数化把代码传递给方法

Java8中增加的另外一个编程概念是通过API来传递代码的能力。

java8增加了把方法(你的代码)作为参数传递给另一个方法的能力。下图则是描述了这样的一种思想。我们把这一概念行为实现参数化的思想上。

HYgo4A.png

java中的函数

编程语言中的函数一词通常是指方法,尤其是值静态方法;这是在数学函数,也就是没有副作用的函数之外的新含义。

Java8的一个新功能是方法引用。

File[] hiddenFiles = new File(".").listFiles(new FileFilter(){
    public boolean accept(File file){
        return file.isHidden();
    }
})

在Java8中可以将代码进行重写:

File[] HiddenFiles = new File(".").listFiles(File::isHidden);
流初识

例子:你需要从一个列表中筛选金额较高的交易,然后按货币分组。

HYhrSs.png

使用流:

HYh6O0.png

通过行为参数化传递代码

应对不断变化的需求

筛选绿苹果
public static List<Apple> filterGreenApples(List<Apple> inventory){
		// 创建一个集合存储苹果
		List<Apple> result = new ArrayList<>();
		for(Apple apple: inventory){
			if("green".equals(apple.getColor())){
				result.add(apple);
			}
		}
		return result;
	}

但是接下来又想筛选出多种颜色:比如浅绿色、暗红色、黄色等。

将颜色作为参数
public static List<Apple> filterApplesByColor(List<Apple> inventory, String color){
		List<Apple> result = new ArrayList<>();
		for(Apple apple: inventory){
			if(apple.getColor().equals(color)){
				result.add(apple);
			}
		}
		return result;
	}
对大于150克的苹果进行筛选
public static List<Apple> filterApplesByWeight(List<Apple> inventory, int weight){
		List<Apple> result = new ArrayList<>();
		for(Apple apple: inventory){
			if(apple.getWeight() > weight){
				result.add(apple);
			}
		}
		return result;
	}

行为参数化

行为参数化就是帮助你处理频繁变更的需求的一种软件开发模式。它意味着拿出一个代码块,把它准备好却不去执行它。这个代码块以后可以被你程序的其他部分调用,着意味着你可以推迟着块代码块的执行。

面对上面的例子,一种可能的解决方案是对你的选择标准进行建模:你考虑得是苹果,需要根据Apple的某些属性来返回一个boolean值。我们把它们称为谓词。定义一个接口来对选择标准建模。

public interface ApplePredicate{
		public boolean test(Apple a);
	}

多个实现选择不同的标准:

static class AppleWeightPredicate implements ApplePredicate{
		public boolean test(Apple apple){
			return apple.getWeight() > 150; 
		}
public static class AppleColorPredicate implements ApplePredicate{
		public boolean test(Apple apple){
			return "green".equals(apple.getColor());
		}
	}

怎么利用ApplePredicate的不同实现呢?需要filterApples方法接受ApplePredicate对象,对App做条件测试。这就是行为参数:让方法接受多种行为作为参数,并在内部使用,来完成不同的行为。

根据抽象条件筛选

public static List<Apple> filterApples(List<Apple> inventory,ApplePredicate p){
    List<Apple> result = new ArrayList<>();
    for(Apple apple:inventory){
        if(p.test(apple)){
            result.add(apple);
        }
    }
    return result;
}

传递代码行为:

如果一个农民工让你找出所有重量超过150克的红苹果,你只需要创建一个实现类来实现ApplePredicate就行了。

public static class AppleRedAndHeavyPredicate implements ApplePredicate{
		public boolean test(Apple apple){
			return "red".equals(apple.getColor()) 
					&& apple.getWeight() > 150; 
		}
	}

List<Apple> redAndHeavyApples = filter(inventory, new AppleRedAndHeavyPredicate());
		System.out.println(redAndHeavyApples);

上述中比较重要的点就是传递的是对象,通过实现test方法的对象来传递布尔表达式。

HUUaVI.png

多种行为,一个参数

HUUbL9.png

测试:

编写一个prettyPrintApple方法,它接受一个Apple的List,并可以对它参数化,以多种方式根据苹果生成一个String输出。

打印每个苹果的重量,并判断式重的还是轻的。

编写代码前的框架:

public static void prettyPrintApple(List<Apple> inventory,???){
    for(Apple apple: inventory){
        String output = ???.???(apple);
        System.out.println(output);
    }
}

步骤:

需要一种接受apple并且返回String 值得方法。

public interface AppleFormatter{
    String accept(Apple a);
}

通过实现AppleFormatter方法,来表示多种格式行为了:

public class AppleFancyFormatter implements AppleFormatter{
    public String accept(Apple apple){
        String char = apple.getWeight() > 150 ? "heavy":"light";
        return "A "+char + " "+apple.getColor()+ "apple";
    }
}

public class AppleSimpleFormatter implements AppleFormatter{
    public String accept(Apple a){
        return "An apple of"+apple.getWeight()+"g";
    }
}

最后,让上述得prettyPrintApple方法接受AppleForamtter对象,并在内部使用它们

public static void prettyPrintApple(List<Apple> inventory,AppleFormatter formatter){
    for(Apple apple: inventory){
        String output =formater.accept(apple);
        System.out.println(output);
    }
}

对于上述得代码进行分析,我们发现当我们把行为传递给filterApples方法的时候,还是不得不实现好几个ApplePredicate接口的类。

行为化参数,用谓词筛选苹果

HUrGk9.png

面对上述的代码我们可以使用匿名类。

尝试使用Lambda表达式
List<Apple> result = filterApples(inventory,(Apple apple)->"red".equals(apple.getColor()));
尝试将List类型抽象化

上述的filterApples方法还只适用于Apple,你还可以将List类型进行抽象化,把办法运用在香蕉、桔子等。

public interface Predicate<T>{
    boolean test(T t);
}

public static <T> List<T> filter(List<T> list, Predicate<T> p){
		List<T> result = new ArrayList<>();
		for(T e: list){
			if(p.test(e)){
				result.add(e);
			}
		}
		return result;
	}

接下来可以将对应的参数传入其中:

List<Apple> redApples =
			filter(inventory, (Apple apple) -> "red".equals(apple.getColor()));
	List<Integer> evenNumbers =
			filter(numbers, (Integer i) -> i % 2 == 0);

真实的例子

==行为参数化==是一个很有用的模式,它能够轻松地适应不断变化的需求。这种模式可以把一个行为(一段代码)封装起来,并通过传递和使用创建的行为将方法的行为参数化。

用compatator来排序
public interface Comparator<T> {
		public int compare(T o1, T o2);
	}


	inventory.sort(new Comparator<Apple>() {
		public int compare(Apple a1, Apple a2){
			return a1.getWeight().compareTo(a2.getWeight());
		}
	});

// 使用lambda表达式
	inventory.sort(
			(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight()));
用Runnable执行代码块

线程是轻量级的进程:它们自己执行一个代码块。

public interface Runnable{
    public void run();
}

创建接口执行不同行为的线程:

Thread t = new Thread(new Runnable() {
		public void run(){
			System.out.println("Hello world");
		}
	});

// 使用lambda表达式
Thread t = new Thread(() -> System.out.println("Hello world"));
小结
  • 行为参数化,就是一个方法接受多个不同的行为作为参数,并在内部使用它们,完成不同行为的能力。
  • 行为参数化可让代码更好地适应不断变化的要求,减轻未来的工作量。

Lambda表达式

Lambda管中窥豹

Lambda表达式可以理解为可传递的匿名函数的一种方式:它没有名称,但它有参数列表函数主体返回类型,可能还有一个可以抛出的异常列表。

HaF93D.png

未使用lambda表达式之前:

Comparator<Apple> byWeight = new Comparator<Apple>() {
		public int compare(Apple a1, Apple a2){
			return a1.getWeight().compareTo(a2.getWeight());
		}
	};

使用lambda表达式之后:

Comparator<Apple> byWeight =
			(Apple a1, Apple a2) -> a1.getWeight().compareTo(a2.getWeight());

测试:

()->{} // 没有参数,并返回void
()->"QJS" // 没有参数,并返回String作为表达式
()-> {return "Mario";} // 没有参数,并返回String
(Integer i)->return "QJS"+i; //错误,return是一个控制流语句,要使得有效,需要添加(Integer i)->{return "QJS"+i;}
(String s)->{"QJS"} //错误,"QJS"是一个表达式,不是一个语句。修改:(String s)->"QJS"或者 (String s)->{return"QJS";} 

在哪里以及如何使用Lambda

可以在函数式接口上使用lambda表达式

函数式接口

函数式接口就是只定义了一个抽象方法的接口。

JavaAPI常见的一些函数式接口:

HaA5Nt.png

测试:

// 下面哪些是函数式接口:
public interface Adder(
 int add(int a ,int b);
)   // 是函数式接口
 
public interface SmartAdder extends Adder{
    int add(double a,double b);
}
// 不是函数式接口,因为它定义了两个叫做add的抽象方法
public interface Nothing{
    
}
// 不是函数式接口,因为它没有声明抽象方法

函数式接口可以干什么,Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例。

Haebin.png

函数描述符

函数式接口的抽象方法的签名基本上就是Lambda表达式的签名。我们将这种抽象方法叫作函数描述符。例如,Runnbale接口可以看作是一个什么也不接受什么也不返回(void)的函数的签名,因为它只有一个抽象方法,这个方法什么也不接受,什么也不返回。

测试:

HaKiJs.png

把Lambda付诸实践:环绕执行模式

环绕执行模式

[HaRCWQ.png](

[https://imgtu.com/i/HaRCWQ)

HaRNFO.png

第1步:记得行为参数化

从BufferedReader种打印两行的写法:

String result = processFile((BufferedReader br) ->
			br.readLine() + br.readLine());
第2步:使用函数式接口传递行为

Lambda仅用于上下文式函数式接口的情况。因此需要创建一个能匹配BufferedReader->String

@FunctionalInterface
	public interface BufferedReaderProcessor {
		String process(BufferedReader b) throws IOException;
	}
第3步:执行一个行为

BufferedReader->String形式的Lambda满足BufferedReaderProcessor接口中定义的process方法的签名。

public static String processFile(BufferedReaderProcessor p) throws
			IOException {
		try (BufferedReader br =
					 new BufferedReader(new FileReader("data.txt"))) {
			return p.process(br);
		}
	}
第4步:传递Lambda

处理一行:

	String oneLine =
			processFile((BufferedReader br) -> br.readLine());

处理两行:

String twoLines =
			processFile((BufferedReader br) -> br.readLine() + br.readLine());

总结:

[HafYGD.png](

使用函数式接口

Predicate

java.util.function.Predicate 定义了一个名叫test的抽象方法,它接受泛型T对象,并返回一个boolean.

使用Predicate

@FunctionalInterface
public interface Predicate<T>{
    boolean test(T t)
}

public static <T> List<T> filter(List<T> lsit,Predicate<T> p){
    List<T> results = new ArrayList<>();
    for(T s:list){
        if(p.test(s)){
            restults.add(s);
        }
    }
    return results;
}

Predicate<String> nonEmpStringPredicate = (String s)->! s.isEmpty();
Consumer

java.util.function.Consumer定义了一个名叫accept的抽象方法,接受泛型T的对象,但是没有返回值(void)

如果需要访问类型T的对象,并对其进行某些操作,就可以使用这个接口。

// void accept(T t);消费型接口,一个参数,没有返回值
//只有參數,沒有返回值
Consumer<String> consumer = t->{
    System.out.println(t);
};
consumer.accept("javaXXXX");
Function

java.util.function.Function<T,R>接口定义了一个叫做apply的方法,它接受一个泛型T的对象,并且返回一个R的对象。

//R apply(T t);函数型接口,一个参数,一个返回值
//供给型接口
Function<String,Integer> function = t ->{return t.length();};
System.out.println(function.apply("abcd"));
Supplier

Supplie供给型,没有参数,有一个返回值。包含的方法为t.get()

//T get(); 供给型接口,无参数,有返回值
//没有参数,但是有返回值
Supplier<String> supplier =()->{return UUID.randomUUID().toString();};
System.out.println(supplier.get());

方法引用

管中窥豹

根据已有的方法实现类创建Lambda表达式。当使用方法引用时,目标引用放在分隔符**::**前,方法的名称在后。如Apple::getWeight就是引用了Apple类中定义得到方法getWeight.

如何构建方法引用:

方法引用主要有三类:

(1)指向静态方法的方法引用。(如Integer::parseInt)Integer的parseInt方法

(2) 指向任意类型实例方法的方法引用(String类型的length方法,写作String::length)

(String a) -> s.toUpperCase()可以写作String::toUpperCase

(3) 指向现有对象的实例方法的方法引用,(假如你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例getValue,则可以写作expensiveTransaction::getValue)

构造函数引用

对于一个现有构造函数,可以利用它的名称和关键字new来创建它的一个引用: ClassName::new. 它的功能与指向静态方法的引用类似。假如一个构造函数没有签名,它适合Spplier的签名() ->Apple.则可以这样写

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qL57sXZT-1645079970545)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216114312618.png)]

引入流

流是什么

是JavaAPI新成员,它允许你以声明性方式处理数据集合。可以把它看成遍历数据集的高级迭代器。

Java7和Java8的比较:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-i2R7Mwij-1645079970545)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216120005346.png)][HfmHeO.png

可以将几个基础操作连接起来。来表达复杂的数据处理流水线。filter结果被传递给了sorted,sorted的结果被传递给了map,最后传递给collect方法。

流简介

流的简单定义:从支持数据处理数据操作的源生成的元素序列。

  • 元素序列:就像集合一样,流也提供了一个接口,可以访问特定元素类型的一组有序值。
  • 源:流会使用一个提供数据的源,如集合、数组或输出/输入资源。从有序集合生成流时会保留原有的顺序。列表也是一样。
  • 数据处理操作:流的数据处理功能支持类似于数据库的操作,以及函数式编程语言中的常用操作,如filter、map、reduce、find、match、sort等。

示例:

HfKSZ8.png

HfKFRs.png

分析:首先对menu调用stream方法,由菜单得到一个流。数据源是菜单,它给流提供一个元素序列。接下来,对流应用一些列数据处理操作:filter、map、limit和Collect之外。这些操作之后会返回另一个流,这样他们就可以接成一条流水线。

下图显示了流的操作流程:

HfMkkD.png

  • 😂filter:接受Lambda,从流中排除某些元素。
  • map:接受一个Lambda,将元素转换成其他形式或提取信息。
  • limit:截断流,使其元素不超过指定数量。
  • collect:将流转换为其他形式。

流与集合

  • 集合是一个内存中的数据结构,它包含数据结构中目前所有的值-集合中的每个元素都得先算出来才能添加到集合中。(你可以往集合里加东西或者删除东西,但是不管什么时候,集合中得每个元素都是放在内存里的,元素都得先算出来才能成为集合的一部分)

  • 流则是在概念上固定的数据结构(你不能添加或删除元素),其元素则是按需计算的。流就像是一个延迟创建的集合:只有在消费者要求的时候才会计算值。集合则是急切创建的。

  • 流只能遍历一次。

  • 外部迭代与内部迭代

    使用Collection接口需要用户去做迭代(比如使用for-each),这个称为**外部迭代。**而Stream库使用了内部迭代–它帮你把迭代做了,还把得到的流值存储在某个地方,你只要给出一个函数说要干什么就可以了。下面的代码是这俩种的区别:

    Hf1J9e.png

流操作

HfY5p8.png

HfYq7n.png

使用流

一个流的使用一般包括三件事:

  • 一个**数据源(如集合)**来执行一个查询
  • 一个中间操作链,形成一条流的流水线
  • 一个**终端操作,**执行于流水线,并能生成结果

常用的Stream API操作

HfUMFI.png

使用流

筛选和切片

用谓词筛选

Hfwdkd.png

筛选出各异的元素

流还支持一个叫作distinct的方法,它会返回一个元素各异(根据流所生成元素的hashCode和equals方法实现)。如下面的代码会筛选出**列表中所有的偶数,并保证没有重复。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mqC025RR-1645079970550)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216140056544.png)][外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nZWSzkV9-1645079970550)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216140134117.png)]
截断流

流支持limit(n)方法,该方法会返回一个不超过给定长度的流。所需的长度作为参数传递给limit.如果流是有序的,则最多会返回前n个元素。

HfB5LT.png

HfBjQx.png

跳过元素

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kAUyibJF-1645079970551)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216141201113.png)]

映射

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

流支持map方法,它会接受一个函数作为参数。这个函数会被应用到每个元素上,并讲其映射成一个新的元素。

下面的代码把方法引用Dish::getName传给map方法,用来提前流中菜肴的名称:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QitcKchK-1645079970551)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216142258190.png)]

给定一个单词列表,显示每个单词中有几个字母:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CdMDmenq-1645079970552)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216142503228.png)]

流的扁平化

对于一张单词表,如何返回一张列表,列出里面各不相同的字符呢?

使用flatMap

Hf57JP.png

HfIpiq.png

flatmap方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

查找和匹配

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wanwk3Hu-1645079970553)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220216153241534.png)]

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

allMatch和anyMatch类似,但它会看看流中的元素是否都能匹配给定的谓词。

比如,可以用来看看采品是否有利健康

boolean isHealthy = menu.stream()
    				.allMatch(d -> d.getCalories() < 1000);

noneMatch

和allMatch相对的是noneMatch。它可以确保流中没有任何元素与给定的谓词匹配。

boolean isHealthy = menu.stream()
		.noneMatch(d ->d.getCalories() >= 1000);
查找元素

findAny方法将返回当前流中的任意元素。它可以与其他流操作结婚使用。

可以结合使用filter和findAny方法来实现这个查询:

Optional<Dish> dish = 
      menu.stream()
                .filter(Dish::isVegetarian)
    			.findAny();

HhK5vR.png

查找第一个元素

HhMPVf.png

归约

元素求和

对流中的所有元素求和:

int sum = numbers.stream().reduce(0,(a,b) ->a +b );

reduce接受两个参数:

  • 一个初始值,这里是0
  • 一个BinaryOperator来将两个元素结合起来产生一个新值,这里我们使用的是lambda(a,b) -> a+b.
  • H5ZvJH.png
最大值和最小值

使用reduce来计算流中的最大值:

Optional<Integer> max = numbers.stream().reduce(Integer::max);

H5eThQ.png

如果要计算最小值:

Optional<Integer> max = numbers.stream().reduce(Integer::min);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uv5sUvKl-1645079970555)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220217131112004.png)]

例子:

package lambdasinaction.chap5;
public  class Trader{
	
	private String name;
	private String city;

	public Trader(String n, String c){
		this.name = n;
		this.city = c;
	}

	public String getName(){
		return this.name;
	}

	public String getCity(){
		return this.city;
	}

	public void setCity(String newCity){
		this.city = newCity;
	}

	public String toString(){
		return "Trader:"+this.name + " in " + this.city;
	}
}
package lambdasinaction.chap5;

public class Transaction{

	private Trader trader;
	private int year;
	private int value;

	public Transaction(Trader trader, int year, int value)
	{
		this.trader = trader;
		this.year = year;
		this.value = value;
	}

	public Trader getTrader(){ 
		return this.trader;
	}

	public int getYear(){
		return this.year;
	}

	public int getValue(){
		return this.value;
	}
	
	public String toString(){
	    return "{" + this.trader + ", " +
	           "year: "+this.year+", " +
	           "value:" + this.value +"}";
	}
}
package lambdasinaction.chap5;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

import static java.util.Comparator.comparing;

/**
 * 文件描述
 *
 * @Author: QJS
 * @CreateDate: 2022/2/17 13:19
 **/
public class TrancsactionTest {


    public static void main(String[] args) {
        Trader raoul =  new Trader("Raoul","Cambridge");
        Trader mario = new Trader("Mario","Milan");
        Trader alan = new Trader("Alan","Cambridge");
        Trader brian = new Trader("Brian","Cambridge");

        List<Transaction> transactions = Arrays.asList(
                new Transaction(brian,2011,300),
                new Transaction(raoul,2012,1000),
                new Transaction(raoul,2011,400),
                new Transaction(mario,2012,710),
                new Transaction(mario,2012,700),
                new Transaction(alan,2012,950)
        );

        // 1 找出2011年的所有交易并按交易额排序

        List<Transaction> tr2011 = transactions.stream()
                .filter(transaction -> transaction.getYear() == 2011)
                .sorted(comparing(Transaction::getValue))
                .collect(Collectors.toList());
        System.out.println(tr2011);

        /**
         * 2 交易员都在哪些城市工作过
         */
        List<String> cities = transactions.stream()
                .map(transaction -> transaction.getTrader().getCity())
                .distinct()
                .collect(Collectors.toList());
        System.out.println(cities);

        /**
         * 3 查找所有来自与剑桥的交易员,并按照姓名进行排序
         */

        List<Trader> cambridge1 = transactions.stream()
                .map(Transaction::getTrader)
                .filter(trader -> trader.getCity().equals("Cambridge"))
                .distinct()
                .sorted(comparing(Trader::getName))
                .collect(Collectors.toList());

        System.out.println(cambridge1);

        /**
         *  4 返回所有交易员的姓名字符串,按字母顺序排序
         */
        String reduce = transactions.stream()
                .map(transaction -> transaction.getTrader().getName())
                .distinct()
                .sorted()
                .reduce(" ", (n1, n2) -> n1 + n2);

        System.out.println(reduce);

        /**
         * 5 有没有交易员在米兰工作的
         */
        boolean milan = transactions.stream()
                .anyMatch(transaction -> transaction.getTrader().getCity().equals("Milan"));
        System.out.println(milan);

        /**
         * 6 打印生活在剑桥的交易员的所有交易额
         */
        transactions.stream()
                .filter(transaction -> transaction.getTrader().getCity().equals("Cambridge"))
                .map(Transaction::getValue)
                .forEach(System.out::println);

        /**
         * 7 所有交易中,最大的交易额
         */

        Optional<Integer> maxValue = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::max);
        System.out.println(maxValue);

        /**
         * 8 所有交易中,最小的交易额
         */
        Optional<Integer> minValue = transactions.stream()
                .map(Transaction::getValue)
                .reduce(Integer::min);
        System.out.println(minValue);
    }
}

构建流

由值创建流

可以使用静态方法Stream.of,通过显示值创建一个流。它可以接受任意数量的参数。

将字符串转换为大写字母并且一个个打印出来

Stream<String> stream = Stream.of("Java 8","Lambdas","In","Action");
stream.map(String::toUpperCase).forEach(System.out::println);
由数组创建流

使用静态方法Arrays.stream从数据创建一个流。接受一个数组作为参数。

将一个原始类型int的数组转换成一个IntStream,

int[] numbers = {2,3,5,7,11,13};
int sum = Arrays.stream(numbers).sum();
由文件生成流

H5GHOg.png

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

Stream API 提供了两个静态方法来从函数生成流:Stream.itreate和

Sream.generate.这两个操作可以创建所谓的无限流。

  • 迭代

    Stream.iterate(0,n -> n + 2)
    	.limit(10)
    	.forEach(System.out::println);
    

    iterate方法生成了一个所有正偶数的流:流的第一个元素是初始值0,然后加上2生成新的值2,再加上2来得到新的值4.使用limit来限制大小。调用forEach终端来作消费流。

  • 生成

    generate方法可以让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。

    Stream.generate(Math::random)
    	.limit(5)
    	.forEach(System.out::println);
    

tem.out.println(minValue);
}
}


### 构建流

#### 由值创建流

可以使用静态方法Stream.of,通过显示值创建一个流。它可以接受任意数量的参数。

将字符串转换为大写字母并且一个个打印出来

Stream stream = Stream.of(“Java 8”,“Lambdas”,“In”,“Action”);
stream.map(String::toUpperCase).forEach(System.out::println);


#### 由数组创建流

使用静态方法Arrays.stream从数据创建一个流。接受一个数组作为参数。

将一个原始类型int的数组转换成一个IntStream,

```java
int[] numbers = {2,3,5,7,11,13};
int sum = Arrays.stream(numbers).sum();
由文件生成流

[外链图片转存中…(img-zNI3h7UR-1645079970556)]

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

Stream API 提供了两个静态方法来从函数生成流:Stream.itreate和

Sream.generate.这两个操作可以创建所谓的无限流。

  • 迭代

    Stream.iterate(0,n -> n + 2)
    	.limit(10)
    	.forEach(System.out::println);
    

    iterate方法生成了一个所有正偶数的流:流的第一个元素是初始值0,然后加上2生成新的值2,再加上2来得到新的值4.使用limit来限制大小。调用forEach终端来作消费流。

  • 生成

    generate方法可以让你按需生成一个无限流。但generate不是依次对每个新生成的值应用函数的。

    Stream.generate(Math::random)
    	.limit(5)
    	.forEach(System.out::println);
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值