java 8学习笔记

本文深入介绍了Java8的重要特性,包括方法和lambda表达式的使用,行为参数化,流处理以及并行计算。通过实例展示了如何通过StreamAPI进行数据过滤和转换,并利用并行流实现高效的多线程处理。此外,还探讨了默认方法如何增强接口功能,以及如何应对不断变化的需求。行为参数化通过将代码作为参数传递,提高了代码的灵活性和可复用性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.1 为什么学习java8

java8 之前,必须利用线程才能使用多核。java的演变之路看来一直都是想让并发编程变得更加的容易,出错更少。

java1.0 里面有线程和锁。

java 5 中添加了工业级的构建模块,线程池和并发集合。

java 7 中 fork/join 框架。使得并行变得更加的实用。

例子体验一下java8

  • stream api。
    • 支持许多处理数据的并行操作。
    • 和数据库查询语句的思路类似,用更加高级的方式表达想要的东西。实现通过Stream 库。选择最佳的执行方式。不需要开发者考虑 synchronized 编写代码。
  • 向方法传递代码的技巧。
  • 接口中默认的方法。
1.1 java 怎么还在变化?

c/c++ 仍然是构建操作系统和各种嵌入式系统的流行工具,是因为虽然他们编出来的程序安全性不佳,但是占用的资源少。缺乏安全性可能会导致程序意外崩溃。

1.2 流处理

java8 在 java.util.stream 中添加了一个Stream Api; Stream 就是一系列T 类型的项目。 可以将它看成是一种比较花哨的迭代器。

Stream Api 的很多方法可以连接起来形成一个复杂的流水线,就像是之前例子里面 链接起来的 Unix 命令一样的。

这种编程的做法关键在于,我们可以在一个更加抽象的层次上面编写 java8程序了。

思路变成了 : 怎么把一个流变成另外一个流

  • 自动支持流水线并行操作。
1.3 用行为参数化把代码传递给方法

将方法作为参数传递给

1.4 并行与共享的可变数据
  • 前面stream api 的并行 只有假定你的代码存在多个副本独立运行的情况。
1.5 方法和lambda 作为一等公民

让方法作为值构成其他若干java 8 功能,(如 Stream ) 的基础。

  • 方法引用

    筛选一个目录中的所有隐藏文件demo

java8 之前的实现方式

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

java8

File[] hiddenFiles = new File(".").listFiles(File::isHidden)

说明 我们已经有了函数 isHidden,

::

这里表示的是方法引用 的语法。就是说把这个方法作为值。将这个方法作为值的参数 传递给listFile 方法。

在java8里面写下File::isHidden 的时候,你就创建了一个方法引用,你同样可以传递它。

  • lambda 匿名函数

除了可以命名函数称为一等值之外。java8 另外一个更广义的将函数作为值的思想,包括Lambda 匿名函数。

//表示调用时给定参数 x 就返回 x + 1 的函数
(int x) -> x + 1 

同样的也可以新建一个类 MyMathUtils 这个类里面定义一个add1 方法。‘

MyMathUtils::add1

这种方式也是可以的,但是新的lambda 语法更加的简洁。

总的来说“编写把函数作为一等值来传递的程序

1.5.1传递代码:例子

传递代码说的是,java 8 会把条件代码作为参数传递进去,这样就能够避免filter 方法出现重复代码。

public static boolean isGreenApple(Apple apple) {
	return "green".equls(apple.getColor());
}
public static boolean isHeavyApple(Apple apple) {
 return apple.getWeight() > 150;
} 

//写出来是为了清晰(平常只要从java.util.function导入就可以了)
public interface Predicate<T>{
	boolean test(T t);
} 

// 方法作为Predicate 参数P 传递进去
static List<Apple> filterApples(List<Apple> inventory,Predicate<Apple> p) {
 	List<Apple> result = new ArrayList<>();
		 for (Apple apple: inventory){
 			if (p.test(apple)) {   // 苹果符合 P 所代表的条件吗
 				result.add(apple);
 			}
 	}
 	return result;
}


//调用方式
filterApples(inventory,Apple::isGreenApple);
//或者是
filterApples(inventory,Apple::isHeavyApple);

什么是谓词?

数学上常常用来表示一个类似函数的东西。接受一个参数值,并返回ture 或则是 false。

​ 前面的代码传递了方法 Apple::isGreenApple (接受参数Apple 并返回一个Boolean给FilterApples)

Function<Apple,Boolean>

//使用 Predicate<Apple> 是更标准的方式,效率也会高一点。这避免了把boolean 封装在Boolean 里面。
1.5.2 从传递方法到 Lambda

​ 把方法作为值来传递显然很有用,但是要是为类似于isHeavyApple 和 isGreenApple 这种比较简短的只用一次两次的方法写一堆定义有点烦人,不过java 8 也解决了这个问题。引入了新的方式 匿名函数或者是lambda.

filterApples(inventory,(Apple a) -> "green".equals(a.getColor()));

// 或者
filterApples(inventory, (Apple a) -> a.getWeight() > 150 ); 
// 或者
filterApples(inventory, (Apple a) -> a.getWeight() < 80 || "brown".equals(a.getColor()) );

可以不需要为只用一次的方法写定义;代码更干净,更清晰,因为你不用找自己到底传递了什么代码。

如果方法比较复杂,不能用简短的几行来定义一个方法的话,还是应该用方法引用来指向一个有描述性名称的方法。而不是使用lambda。

以代码的清晰度为准

java 上加上Filter 和几个相关的东西作为通用库方法就足以让人满意了。

filterApples(inventory, (Apple a) -> a.getWeight() > 150 );
// 有相关的类库

static <T> Collection<T> filter(Collection<T> c, Predicate<T> p);

filter(inventory, (Apple a) -> a.getWeight() > 150 );

/* 但是,为了更好地利用并行,Java的设计师没有这么做。Java 8中有一整套新的类集合
API——Stream,它有一套函数式程序员熟悉的、类似于filter的操作,比如map、reduce,还
有我们接下来要讨论的在Collections和Streams之间做转换的方法。*/

​ 但是,为了更好地利用并行,Java的设计师没有这么做。java8中有一整套新的类集合 API——Stream,它有一套函数式程序员熟悉的、类似于filter的操作,比如map、reduce,**还 有我们接下来要讨论的在Collections和Streams之间做转换的方法。

1.6 流

几乎每个程序都会制造和处理集合。集合用起来不总是那么理想。需要从一个列表中筛选出金额较高的交易,然后按照货币分组。

// 建立累积交易分组的Map
Map<Currency, List<Transaction>> transactionsByCurrencies = new HashMap<>();
	// 遍历交易的 List 
	for (Transaction transaction : transactions) {
        // 筛选金额较高的交易
        if(transaction.getPrice() > 1000){
 			// 提取交易的货币
            Currency currency = transaction.getCurrency();
 			List<Transaction> transactionsForCurrency = 			                     transactionsByCurrencies.get(currency);
			 if (transactionsForCurrency == null) {
					transactionsForCurrency = new ArrayList<>();
			 		transactionsByCurrencies.put(currency,transactionsForCurrency);
 		}
        // 将当前遍历的交易添加到具有同一货币的交易 List 中。
 		transactionsForCurrency.add(transaction);
 }
}

Stream Api 的使用

import static java.util.stream.Collectors.toList;
Map<Currency, List<Transaction>> transactionsByCurrencies =
 transactions.stream()
 .filter((Transaction t) -> t.getPrice() > 1000)
 .collect(groupingBy(Transaction::getCurrency)); 

Collection Api 相比,Stream Api 处理数据的方式非常的不同。用集合的话,需要自己做迭代过程。你需要用for-each 循环一个一个做迭代过程。---- 这种迭代方式称为 外部迭代

Stream Api 我们根本不需要操心循环的事情。数据处理完全是在内部进行的。可以把这种思想叫做 内部迭代

1.6.1 多线程并非易事

java8 也用Stream Api 解决了两个问题:集合处理时的套路,难以利用的多线程。其实一些高级的如 filterApples

这些操作是完全可以并行化的。

Collection 主要是 为了存储和访问数据,Stream 主要是用于描述对数据的计算。

几乎免费的并行?

一般是将 collection 转换成 Stream 进行并行处理,然后转换回List

顺序处理

import static java.util.stream.Collectors.toList;
List<Apple> heavyApples =
 inventory.stream().filter((Apple a) -> a.getWeight() > 150)
 .collect(toList()); 

并行处理

import static java.util.stream.Collectors.toList;
List<Apple> heavyApples =
 inventory.parallelStream().filter((Apple a) -> a.getWeight() > 150)
 .collect(toList()); 

java 中的并行与无共享可变状态

函数式编程 中函数的主要意思是 “把函数作为一等值”,第二层意思就是,执行时在元素之间无互动。

1.6.2默认方法

如何改变已经发布的接口而不破坏已有的实现呢?

java8 的解决方法就是打破最后一环。

接口如今可以包含实现类没有提供实现的方法签名了!缺失的方法主体随接口提供了,而不是由实现类提供。

这就给接口设计者提供了一个扩充接口的方式,而不会破坏现有的代码。Java 8在接口声明 中使用新的default关键字来表示这一点。

1.7 函数式编程的其他好思想
  • lambda 作为一等值
  • 没有共享状态的时候,函数或方法可以有效,安全地并行执行。

2 通过行为参数化传递代码

行为参数化可以帮助你处理频繁变更的需求的一种软件开发模式。

2.1 应对不断变化的需求

编写能够应对变化的需求的代码并不容易

2.1.1需求从列表中选出特定属性的元素。

筛选出绿色的苹果

public static List<Apple> filterGreenApples(List<Apple> inventory) {
 List<Apple> result = new ArrayList<Apple>();
     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<Apple>();
     for (Apple apple: inventory){
         if ( apple.getColor().equals(color) ) {
         result.add(apple);
         }
     }
 return result;
} 
// 通过颜色传参可以得到不同颜色的苹果。

List<Apple> greenApples = filterApplesByColor(inventory, "green");
List<Apple> redApples = filterApplesByColor(inventory, "red"); 

通过重量选择苹果,苹果的重量一般是

public static List<Apple> filterApplesByWeight(List<Apple> inventory,
 int weight) {
 List<Apple> result = new ArrayList<Apple>();
     For (Apple apple: inventory){
         if ( apple.getWeight() > weight ){
         result.add(apple);
         }
     }
 return result;
} 

打破了DRY 原则。软件工程角度来说这样太笨重了。

第三次尝试,每个能想到的属性都做筛选。

public static List<Apple> filterApples(List<Apple> inventory, String color,
 int weight, boolean flag) {
 List<Apple> result = new ArrayList<Apple>();
     for (Apple apple: inventory){
         if ( (flag && apple.getColor().equals(color)) ||
         (!flag && apple.getWeight() > weight) ){
         result.add(apple);
         }
     }
 return result;
}

这样写非常的笨重。

List<Apple> greenApples = filterApples(inventory, "green", 0, true); 
2.2 行为参数化

需要一种比添加参数更好的方法来应对需求的变化。

  • 对选择的标准进行建模

    • 需要根据apple 的某些属性来返回一个boolean 值。-----谓词

    定义一个接口对选择标准建模

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

可以用 applepredicate 的多个接口的实现代表不同的选择标准了。

选出苹果的重量

public class AppleHeavyWeightPredicate implements ApplePredicate{ 
 public boolean test(Apple apple){ 
 return apple.getWeight() > 150; 
 } 
} 

选出绿色苹果

public class AppleGreenColorPredicate implements ApplePredicate{
 public boolean test(Apple apple){ 
 return "green".equals(apple.getColor()); 
 } 
} 

上面写的两种方式和策略模式比较相关,它让你定义一族算法,把它们封装起来,然后在运行时选择一个算法。

不同的策略就是AppleHeavyWeightPredicate 和 AppleGreenColorPredicate

如何利用 applePredicate的不同实现,需要让filterApples 方法接受applePredicate 对象,对apple做条件测试。这就是行为参数化。让方法接受多种行为。或者是参数,并在内部使用。来完成不同的行为。

java8 collection 接口

removeif() 示例

作用:
删除集合中符合条件的成员,empty集合也可以,但不能是null。

   /**
     * 删除集合中符合条件的成员,empty集合也可以。
     */
    private static void removeIfTest() {
        List<String> list = Lists.newArrayList("1","12","13","14","15","0");
        System.out.println("初始时:"+ list.toString());
        list.removeIf(s -> s.contains("1"));
        System.out.println("过滤完:" + list.toString());
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值