nico对《Java 8函数式编程》的笔记(33)

Java 8函数式编程
  • 书名: Java 8函数式编程
  • 作者: [英] Richard Warburton
  • 页数: 148
  • 出版社: 人民邮电出版社
  • 出版年: 2015-3
  • 第1页
    Q: 为什么要会有Java 8?
    A: 适应现代多核CPU,让代码在多核CPU上高效运行。
    面向对象编程是对数据进行抽象,而函数式编程是对行为进行抽象。
    以前传递行为的方式是传递拥有具有某种行为的对象,Java 8有了Lambda表达式和方法引用(因为Lambda表达式和方法引用都是直接表述一种行为)后,传递行为的方式更加直观。
    2016-07-17 22:36:28 回应
  • 第8页
    Lambda表达式引用的是值,而不是变量。
    如果Lambda表达式引用表达式外部的变量,那这个变量只能是一个既成事实上的final变量。
    String name = getUserName();
    name = formatUserName(name);
    button.addActionListener(event -> System.out.println("hi " + name));
    
    以上代码无法通过编译,name可以不用final声明,但是必须和final一样,只能被赋一次值。
    2016-07-17 22:34:00 回应
  • 第9页
    函数接口是只有一个抽象方法的接口,用作Lambda表达式的类型。
    这就是函数接口,接口中单一方法的命名并不重要,只要方法签名和Lambda表达式的类型匹配即可。
    2016-07-18 08:05:36 回应
  • 第10页
    例 2-10 使用菱形操作符,根据方法签名做推断
    userHashmap(new HashMap<>());
    
    ...
    
    
    private void userHashmap(Map<String, String> values);
    
    Java 8中对类型推断系统的改善值得一提。上面的例子将new HashMap<>()传给userHashmap方法,即使编译器拥有足够的信息,也无法在Java 7中通过编译。
    例 2-12 Predicate接口的源码,接受一个对象,返回一个布尔值
    public interface Predicate<T> {
          boolean test(T t);
    }
    
    BinaryOperator接口,需要接受一个泛型,若没有写明泛型,则默认为Object。
    @FunctionalInterface
    public interface BinaryOperator<T> extends BiFunction<T,T,T> {
        /**
         * Returns a {@link BinaryOperator} which returns the lesser of two elements
         * according to the specified {@code Comparator}.
         *
         * @param <T> the type of the input arguments of the comparator
         * @param comparator a {@code Comparator} for comparing the two values
         * @return a {@code BinaryOperator} which returns the lesser of its operands,
         *         according to the supplied {@code Comparator}
         * @throws NullPointerException if the argument is null
         */
        public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
        }
    
        /**
         * Returns a {@link BinaryOperator} which returns the greater of two elements
         * according to the specified {@code Comparator}.
         *
         * @param <T> the type of the input arguments of the comparator
         * @param comparator a {@code Comparator} for comparing the two values
         * @return a {@code BinaryOperator} which returns the greater of its operands,
         *         according to the supplied {@code Comparator}
         * @throws NullPointerException if the argument is null
         */
        public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
        }
    }
    
    2016-07-18 08:27:42 回应
  • 第13页
    2.ThreadLocal Lambda表达式。Java有一个ThreadLocal类,作为容器保存了当前线程里局部变量的值。Java 8为该类新加了一个工厂方法,接受一个Lambda表达式,并产生一个新的ThreadLocal对象,而不用使用继承,语法上更加简洁。
    a. 在Javadoc或集成开发环境(IDE)里找出该方法。
    b. DateFormatter类是非线程安全的。使用构造函数创建一个线程安全的DateFormatter对象,并输出日期,如“01-Jan-1970”。
    ThreadLocal<DateFormatter> threadLocal = ThreadLocal
    	.withInitial(() -> new DateFormatter(new SimpleDateFormat("dd-MMM-yyyy")));
    System.out.println(threadLocal.get().valueToString(new Date()));
    
    2016-07-20 21:13:36 回应
  • 第19页
    3.3.1 collect(toList())
    List<String> collected = Stream.of("a", "b", "c").collect(Collectors.toList());
    
    3.3.2 map
    如果有一个函数可以将一种类型的值转换成另外一种类型,map操作就可以使用该函数,将一个流中的值转换成一个新的流。
    List<String> strList = Arrays.asList("str1", "str2", "str3", "str4", "str5", "str6");
    List<Integer> intList = strList.stream()
          .map(s -> Integer.parseInt(String.valueOf(s.charAt(3))))
          .collect(Collectors.toList());
    
    3.3.4 flapMap
    flatMap方法可用Stream替换值,然后将多个Stream连接成一个Stream。
    List<Integer> integerResult = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4))
    				.flatMap(n -> n.stream())
    				.collect(Collectors.toList());
    		System.out.println("integerResult: " + integerResult);
    
    相当于下面代码:
    Stream<List<Integer>> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4));
    Stream<Integer> integers = together.flatMap(n -> n.stream());
    List<Integer> integerList = integers.collect(Collectors.toList());
    System.out.println("integerList: " + integerList);
    
    flatMap中的方法返回的是Stream类型,也就是把所有返回的Stream对象全部串成一个Stream对象作为结果。
    3.3.5 max和min
    List<String> articles = Arrays.asList("Jack", "Linda", "Mark", "JJ", "M");
    Optional<String> minOption = articles.stream()
          .min(Comparator.comparing(a -> a.length()));
    String minStr = minOption.get();
    System.out.println("minStr: " + minStr);
    
    comparing方法接受一个函数并返回一个函数。
    3.3.7 reduce ——重点、难点
    reduce操作可以实现从一组值中生成一个值。
    count()、min()和max()方法都是reduce操作,因其常用而被纳入标准库中。
    int count_1 = Stream.of(1, 2, 3, 4, 5)
    	.reduce(0, (acc, name) -> acc + name);
    System.out.println("count_1: " + count_1);
    
    T reduce(T identity, BinaryOperator<T> accumulator);
    
    以下为BinaryOperator接口源码:
    @FunctionalInterface
    public interface BinaryOperator<T> extends BiFunction<T,T,T> {
        public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
        }
        public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
            Objects.requireNonNull(comparator);
            return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
        }
    }
    
    BinaryOperator接口并没有抽象方法,所以它的抽象方法只能在其父类或父类的父类中。
    以下为BiFunction接口源码:
    @FunctionalInterface
    public interface BiFunction<T, U, R> {
        R apply(T t, U u);
    
        default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
            Objects.requireNonNull(after);
            return (T t, U u) -> after.apply(apply(t, u));
        }
    }
    
    所以 BinaryOperator<T>接口实现的是T apply(T t, T u);方法,因此,当reduce方法的参数为两个时,操作的只能都是同类型的参数,返回同类型的值。
    int count_2 = Stream.of("Jack", "Linda", "Mark", "JJ", "M")
    	.reduce(0, (acc, s) -> acc + s.length(), (a, b) -> a + b);
    System.out.println("count_2: " + count_2);
    
    reduce方法的参数为三个时
    <U> U reduce(U identity,
                     BiFunction<U, ? super T, U> accumulator,
                     BinaryOperator<U> combiner);
    
    所以BiFunction<U, ? super T, U>接口实现的是U apply(U u, ? super T t);方法,其中T类型是Stream<T>中的T,所以(acc, s)中可以指导s是String类型。
    2016-07-22 22:02:03 回应
  • 第31页
    高阶函数是指接受另外一个函数作为参数,或返回一个函数的函数。
    如果函数的参数列表里包含函数接口,或该函数返回一个函数接口,那么该函数就是高阶函数。
    Stream接口中几乎所有函数都是高阶函数。
    2016-07-23 10:13:03 回应
  • 第31页
    本章介绍的概念能够帮助用户写出更简单的代码,因为这些概念描述了数据上的操作,明确了要达成什么转化,而不是说明如何转化。这种方式写出的代码,潜在的缺陷更少,更直接地表达了程序员的意图。
    明确要达成什么转化,而不是说明如何转化的另外一层含义在于写出的函数没有副作用。
    没有副作用的函数不会改变程序或外界的状态。
    Lambda表达式中向控制台输出信息或给局部变量赋值都是有副作用的。
    无论何时,将Lambda表达式传给Stream上的高阶函数,都应该尽量避免副作用。唯一的例外是forEach方法,它是一个终结方法。
    2016-07-23 11:02:13 回应
  • 第33页
    1.只用reduce和Lambda表达式写出实现Stream上的map操作的代码,如果不想返回Stream,可以返回一个List。
    @Test
    public void test1() {
    	List<String> names = Arrays.asList("Jack", "Linda", "Mark", "JJ", "M");
    	List<Integer> lengths = likeMap(names, s -> s.length());
    	System.out.println("lengths: " + lengths);
    }
    	
    public <T, R> List<R> likeMap(List<T> resource, Function<T, R> function) {
    	return resource.stream().reduce(new ArrayList<R>(), (acc, x) -> {
    		List<R> newAcc = new ArrayList<>(acc);
    		newAcc.add(function.apply(x));
    		return newAcc;
    	}, (List<R> a, List<R> b) -> {
    		List<R> left = new ArrayList<>(a);
    		left.addAll(b);
    		return left;
    	});
    }
    
    2.只用reduce和Lambda表达式写出实现Stream上的filter操作的代码,如果不想返回Stream,可以返回一个List。
    @Test
    public void test2() {
    	List<String> names = Arrays.asList("Jack", "Linda", "Mark", "JJ", "M");
    	//过滤不含'a'字符的字符串
    	List<String> result = likeFilter(names, s -> s.contains("a"));
    	System.out.println("result: " + result);
    }
    
    public <T> List<T> likeFilter(List<T> resorce, Predicate<? super T> predicate) {
    	return resorce.stream().reduce(new ArrayList<T>(), (acc, x) -> {
    		List<T> newAcc = new ArrayList<>(acc);
    		if (predicate.test(x))
    			newAcc.add(x);
    		return newAcc;
    	}, (List<T> left, List<T> right) -> {
    		List<T> list = new ArrayList<>(left);
    		list.addAll(right);
    		return list;
    	});
    }
    
    2016-07-23 13:27:45 回应
  • 第36页
    Java 8提供了类型转换的函数接口。
    ToIntFunction接口:
    @FunctionalInterface
    public interface ToIntFunction<T> {
        int applyAsInt(T value);
    }
    
    IntFunction接口:
    @FunctionalInterface
    public interface IntFunction<R> {
        R apply(int value);
    }
    
    在Java 8中,仅对整型、长整型和双浮点型做了特殊处理。
    @Test
    public void test4_4() {
    	List<Integer> ints = Arrays.asList(1, 2, 3, 4, 5);
    	System.out.println(ints.stream()
                  .mapToInt(num->num+1).summaryStatistics().getMax());
    }
    
    其中mapToInt方法中的参数就是ToIntFunction接口。
    2016-07-23 14:53:20 回应
<前页 1 2 3 4 后页>

nico的其他笔记  · · · · · ·  ( 全部41条 )