Java8实战笔记-1

2019-01-18  本文已影响78人  王小杰at2019

行为参数化

Lambda表达式

  1. lambda
    • 参数列表——这里它采用了 Comparator 中 compare 方法的参数,两个 Apple 。
    • 箭头——箭头 -> 把参数列表与Lambda主体分隔开。
    • Lambda主体——比较两个 Apple 的重量。表达式就是Lambda的返回值了。
lambda
  1. 比较两个苹果重量的例子

先前:

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());
  1. lambda好处
  1. 示例

根据上述语法规则,以下哪个不是有效的Lambda表达式?

答案:只有4和5是无效的Lambda。

 在哪里以及如何使用Lambda

  1. 函数式接口

    函数式接口就是只定义一个抽象方法的接口。你已经知道了Java API中的一些其他函数式接口例如 Comparator 和 Runnable 。 如果你去看看新的Java API,会发现函数式接口带有@FunctionalInterface 的标注

    Lambda表达式允许你直接以内联的形式为函数式接口的抽象方法提供实现,并把整个表达式作为函数式接口的实例(具体说来,是函数式接口一个具体实现的实例)。你用匿名内部类也可以完成同样的事情,只不过比较笨拙

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

    我们在本章中使用了一个特殊表示法来描述Lambda和函数式接口的签名。 () -> void 代表
    了参数列表为空,且返回 void 的函数。这正是 Runnable 接口所代表的。 举另一个例子, (Apple,
    Apple) -> int 代表接受两个 Apple 作为参数且返回 int 的函数。我们会在3.4节和本章后面的
    表3-2中提供关于函数描述符的更多信息

 环绕执行模式

  1. 记得行为参数化
  2. 使用函数式接口来传递行为
  3. 执行一个行为
  4. 传递 Lambda

@FunctionalInterface
interface RowMap<T> {
    T mapped(ResultSet resultSet) throws SQLException;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Teacher {
    String name;
    int age;
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class User {
    String name;
    int age;
}

class QueryUtil {
    public <T> List<T> getList(String sql, RowMap<T> rm) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        PreparedStatement preparedStatement = connection.prepareStatement(sql);
        ResultSet resultSet = preparedStatement.executeQuery();
        List<T> temp = new ArrayList<>();
        while (resultSet.next()) {
            T mapped = rm.mapped(resultSet);
            temp.add(mapped);
        }
        return temp;
    }

}

public class Around1 {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        QueryUtil userDao = new QueryUtil();
        List<User> list = userDao.getList("SELECT * FROM user",
                resultSet ->
                        new User(resultSet.getString("name"), resultSet.getInt("age")
                        )
        );

        System.out.println(JSON.toJSONString(list));
        List<Teacher> teachers = userDao.getList("SELECT * FROM teacher",
                resultSet ->
                        new Teacher(resultSet.getString("name"), resultSet.getInt("age")
                        )
        );
        System.out.println(JSON.toJSONString(teachers));
    }

}


使用函数式接口

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

  2. Consumer Consumer<T> T->void
    java.util.function.Consumer<T> 定义了一个名叫 accept 的抽象方法,它接受泛型 T
    的对象,没有返回( void )。

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

//Predicate T->boolean  
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 67, 8, 9);
List<Integer> collect = list.stream().filter((a -> a > 5)).collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect));

//Consumer  T->void 
list.forEach(a -> {
    System.out.println(a);
});

//接收接收一个T类型,返回一个R类型   T->R 
List<String> collect1 = list.stream().map(a -> a + 1+"").collect(Collectors.toList());
System.out.println(JSON.toJSONString(collect1));

方法引用

Lambda及其等效方法引用的例子

Lambda 等效的方法引用
(Apple a) -> a.getWeight() Apple::getWeight
() -> Thread.currentThread().dumpStack() Thread.currentThread()::dumpStack
(str, i) -> str.substring(i) String::substring
(String s) -> System.out.println(s) System.out::println
  1. 指向静态方法的方法引用(例如 Integer 的 parseInt 方法,写作 Integer::parseInt )。
  2. 指 向 任 意 类 型 实 例 方 法 的 方 法 引 用 ( 例 如 String 的 length 方 法 , 写 作
    String::length )。
  3. 指向现有对象的实例方法的方法引用(假设你有一个局部变量 expensiveTransaction
    用于存放 Transaction 类型的对象,它支持实例方法 getValue ,那么你就可以写 expensive-
    Transaction::getValue )。
appleList.stream().map(Apple::getColor).forEach(System.out::println);
appleList.forEach(System.out::println);

Lambda复合

什么是流

流是Java API的新成员,它允许你以声明性方式处理数据集合(通过查询语句来表达,而不
是临时编写一个实现),就现在来说,你可以把它们看成遍历数据集的高级迭代器。此外,流还可以透明地并行处理,你无需写任何多线程代码了!

其他库:Guava、Apache和lambdaj

为了给Java程序员提供更好的库操作集合,前人已经做过了很多尝试。比如,Guava就是
谷歌创建的一个很流行的库。它提供了 multimaps 和 multisets 等额外的容器类。Apache
Commons Collections库也提供了类似的功能。最后,本书作者Mario Fusco编写的lambdaj受到
函数式编程的启发,也提供了很多声明性操作集合的工具。
如今Java 8自带了官方库,可以以更加声明性的方式操作集合了。

“从支持数据处理操作的源生成的元素序列”。让
我们一步步剖析这个定义。

image

集合与流

请注意,和迭代器类似,流只能遍历一次。遍历完之后,我们就说这个流已经被消费掉了

 Stream<Apple> stream = appleList.stream();
        stream.forEach(System.out::println);
        stream.forEach(System.out::println);
        

Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.util.stream.AbstractPipeline.sourceStageSpliterator(AbstractPipeline.java:279)
    at java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:580)
    at cn.wyj.jdk7.TestFunction.main(TestFunction.java:59)

内部迭代与外部迭代

image

中间操作与终端操作

中间操作

诸如 filter 或 sorted 等中间操作会返回另一个流。这让多个操作可以连接起来形成一个查
询。重要的是,除非流水线上触发一个终端操作,否则中间操作不会执行任何处理——它们很懒。
这是因为中间操作一般都可以合并起来,在终端操作时一次性全部处理。

image

终端操作

端操作会从流的流水线生成结果。其结果是任何不是流的值,比如 List 、 Integer ,甚
至 void 。例如,在下面的流水线中, forEach 是一个返回 void 的终端操作,它会对源中的每道
菜应用一个Lambda。把 System.out.println 传递给 forEach ,并要求它打印出由 menu 生成的
流中的每一个 Dish :
menu.stream().forEach(System.out::println);

使用流

筛选、切片和匹配

//筛选
List<Dish> vegetarianMenu = menu.stream()
.filter(Dish::isVegetarian)
.collect(toList());


//去重
List<Integer> numbers = Arrays.asList(1, 2, 1, 3, 3, 2, 4);
numbers.stream()
.filter(i -> i % 2 == 0)
.distinct()
.forEach(System.out::println);

//截短流
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.limit(3)
.collect(toList());

// 跳过
List<Dish> dishes = menu.stream()
.filter(d -> d.getCalories() > 300)
.skip(2)
.collect(toList());

映射

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

//如果你要找出每道菜的名称有多长

List<Integer> dishNameLengths = menu.stream()
.map(Dish::getName)
.map(String::length)
.collect(toList());

流的扁平化

使用 flatMap 方法的效果是,各个数组并不是分别映射成一个流,而是映射成流的内容。所
有使用 map(Arrays::stream) 时生成的单个流都被合并起来,即扁平化为一个流。

List<String> uniqueCharacters =
words.stream()
.map(w -> w.split(""))
.flatMap(Arrays::stream)
.distinct()
.collect(Collectors.toList());

查找、匹配和归约

使用数值范围等数值流

从多个源创建流

无限流

上一篇 下一篇

猜你喜欢

热点阅读