0
点赞
收藏
分享

微信扫一扫

Java8 函数式编程


Java8 函数式编程

1. Java8 接口变化(default)

先看下Java8 中的迭代器接口中的默认方法

public interface Iterable<T> {
    /**
     * Returns an iterator over elements of type {@code T}.
     *
     * @return an Iterator.
     */
    Iterator<T> iterator();

    /**
     * Performs the given action for each element of the {@code Iterable}
     * until all elements have been processed or the action throws an
     * exception.  Actions are performed in the order of iteration, if that
     * order is specified.  Exceptions thrown by the action are relayed to the
     * caller.
     * <p>
     * The behavior of this method is unspecified if the action performs
     * side-effects that modify the underlying source of elements, unless an
     * overriding class has specified a concurrent modification policy.
     *
     * @implSpec
     * <p>The default implementation behaves as if:
     * <pre>{@code
     *     for (T t : this)
     *         action.accept(t);
     * }</pre>
     *
     * @param action The action to be performed for each element
     * @throws NullPointerException if the specified action is null
     * @since 1.8
     */
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    /**
     * Creates a {@link Spliterator} over the elements described by this
     * {@code Iterable}.
     *
     * @implSpec
     * The default implementation creates an
     * <em><a href="../util/Spliterator.html#binding">early-binding</a></em>
     * spliterator from the iterable's {@code Iterator}.  The spliterator
     * inherits the <em>fail-fast</em> properties of the iterable's iterator.
     *
     * @implNote
     * The default implementation should usually be overridden.  The
     * spliterator returned by the default implementation has poor splitting
     * capabilities, is unsized, and does not report any spliterator
     * characteristics. Implementing classes can nearly always provide a
     * better implementation.
     *
     * @return a {@code Spliterator} over the elements described by this
     * {@code Iterable}.
     * @since 1.8
     */
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

2. Lambda表达式

Java8 之前字符串排序

@Test
public void test01() {
    List<String> names = Arrays.asList("peter", "hanmei", "anan", "mike");

    // 匿名内部类
    Collections.sort(names, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareTo(o2);
        }

    });
    System.out.println(names);
}

在Java8 之后可以使用lambda表达式,简洁写法:

Collections.sort(names, (String o1,String o2) -> {
    return o1.compareTo(o2);
});

更加简洁写法:

Collections.sort(names, (o1, o2) -> o1.compareTo(o2));

你觉得这样就完了?可以变的更短更具有可读性:

names.sort((o1,o2)->o1.compareTo(o2));

3. 函数式接口

Java语言设计这们投入大量的精力来思考如何使现有的函数友好的支持Lambda。最终采用的方法使:增加函数式接口;

“函数式接口” 指 仅仅只包含一个抽象方法,但是可以有多个非抽象方法(默认方法)的接口。

像这样的接口,可以被隐式转换为Lambda表达式。

java.lang.Runnable 与 Java.util.concurrent.Callable 就是典型的函数式接口;

提示:Java8 增加了一种特殊的注解@FunctionalInterface, 这个注解通常不是必须的,只要接口只包含一个抽象方法,虚拟机都会自动判断该接口为函数式接口。一般建议加上,方便编译期报错;

@FunctionalInterface
public interface Converter<F, T> {
    T  convert(F from);
}

// 将数字字符串转换为整数类型
Converter<String,Integer> converter = (from) -> Integer.valueOf(from);
Integer converted = converter.convert("123");
System.out.println(converted);

3.1 Lambda 表达式作用域

3.1.1 访问局部变量

可以在Lambda表达式中访问外部局部变量:

// lambda 方法外部变量,这里不需要声明为final类型
String num = "100";
Converter<String,Integer> intConverter = (from) -> Integer.valueOf(from + num);
System.out.println(intConverter.convert("123"));

虽然没有使用final修饰,但是lambda表达式中仍不可修改(隐式final)

String num = "100";
Converter<String,Integer> intConverter2 = (from) -> {
    num = "101"; // 这里直接编译报错
    return Integer.valueOf(from + num);
};

3.1.2 访问静态变量

与局部变量相比,lambda表达式中的实例变量与静态变量都有读写权限。该行为与匿名内部类一致;

class Lambda2{
	static int staticNum;
	int num;
	
	public void testScopes() {
		Converter<String,Integer> c1 = (from) -> {
			staticNum = 123;
			return Integer.valueOf(staticNum + from);
		};
		
		Converter<String,Integer> c2 = (from) -> {
			num = 123;
			return Integer.valueOf(num + from);
		};
	}
}

4. Java8 内置函数式接口

4.1 Predicate ( 断言 )

Predicate 接口只提供了一个返回值为Boolean类型的断言型接口。但是该接口包含很多默认方法。
源码:

package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Predicate<T> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

    //与 && 类型,都是true 才返回 true
    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }

    // 结果取反
    default Predicate<T> negate() {
        return (t) -> !test(t);
    }

  	// || 类似,只要有一个成立就返回true
    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    // 判断是否相等
    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }

    /**
     *
     * @since 11
     */
    @SuppressWarnings("unchecked")
    static <T> Predicate<T> not(Predicate<? super T> target) {
        Objects.requireNonNull(target);
        return (Predicate<T>)target.negate();
    }
}

示例:

Predicate<String> predicate = s -> s.length() > 0;
predicate.test("123"); // true
predicate.negate().test("foo");  // false

Predicate<Boolean> nonNull = Objects::nonNull;
Predicate<Boolean> isNull = Objects::isNull;
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> notEmpty = isEmpty.negate();

nonNull.test(null); // false
isNull.test(null); // true
isNull.negate().test(null); // false

4.2 Function (函数)

Function 接口接受一个参数并生成结果。默认方法可以将多个函数连接在一起(compose, andThen)

源码:

package java.util.function;

import java.util.Objects;

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

    /**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

   // 整个两个function
    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

   //执行完之后执行
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    //直接返回
    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

示例:

Function<String,Integer> toInteger = Integer::valueOf;
toInteger.apply("123"); // 123
toInteger.andThen(String::valueOf).apply("123"); // "123"
toInteger.andThen(c-> c + "test").apply("123"); // 123test

4.3 Supplier (生产)

Supplier 接口产生给定泛型类型的结果。与Function接口不同,Supplier接口不接受参数;

源码:

package java.util.function;

/**
 * Represents a supplier of results.
 *
 * <p>There is no requirement that a new or distinct result be returned each
 * time the supplier is invoked.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #get()}.
 *
 * @param <T> the type of results supplied by this supplier
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

示例:

Supplier<String> strSupplier = String::new;
strSupplier.get();

4.4 Consumer (消费)

Consumer 接口表示要对单个输入参数执行的操作;

源码:

package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Consumer<T> {

    /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

    
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

示例:

Consumer<String> printString = p -> System.out.println(p + ": string");
printString.accept("java");

5. Stream 流式编程

5.1 什么是stream?

Stream 是Java8 中处理数组、集合的抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤、映射数据等操作。使用Stream API 对集合数据进行操作,就类似与使用SQL执行的数据库查询。

Stream 的特点?

  1. Stream 本身不会存储元素;
  2. Stream 不会改变源对象,相反,他会返回一个持有结果的新Stream;
  3. Stream 操作是延迟执行的,这意味着他会等需要结果的时候才执行;

Stream 遵循“做什么,而不是怎么做”的原则。只需要描述需要做什么。而不用考虑程序是怎么实现的。

5.2 使用Stream API 的步骤

  1. 创建Stream。 - 创建
  2. 在一个或者多个步骤中,将初始Stream转化到另外一个Stream的中间操作。- 中间操作
  3. 使用一个终止操作来产生一个结果。该操作会强制之前的延迟操作立即执行,在这之后,该Stream就不会再被使用了。- 终止操作

5.3 Stream 的创建

// 创建Stream 的方式
// 1. Collection 接口 stream(), parallelStream()
List<String> nameList = Arrays.asList("tom", "jerry", "coco", "nick");
Stream<String> stream = nameList.stream();
Stream<String> parallelStream = nameList.parallelStream();

// 2. Arrays.stream()
Stream<Integer> intStream = Arrays.stream(new Integer[] {10,23,34,15,6,45,98,32});

// 3. Stream.of()
Stream<String> stream2 = Stream.of("aa", "bb", "cc");

// 4. Stream.iterate(0, x-> x+1)
Stream<Integer> stream3 = Stream.iterate(0, x -> x+1);

// 5. Stream.generate(()->Math.random())
Stream<Double> stream4 = Stream.generate(()->Math.random());

5.4 Stream 中间操作

5.4.1 筛选切片

  1. filter 过滤
  2. limit 截断流,使其元素不会超过指定数量
  3. skip(n) 跳过元素
  4. distinct 去重

5.4.2 映射

  1. map 映射
  2. flatMap 映射

5.4.3 排序

  1. sorted 自然排序
  2. sort 指定排序

示例:

实体类:

@Getter
@Setter
@AllArgsConstructor
@ToString
class Person{
    private String name;
    private Integer age;
}

测试:

@Test
public void test02() {
    List<Person> personList = new ArrayList<>();
    personList.add(new Person("zhangsan", 18));
    personList.add(new Person("lisi", 28));
    personList.add(new Person("wangwu", 26));
    personList.add(new Person("zhaoliu", 32));

    // 筛选 年龄 > 20
    personList.stream().filter(p->p.getAge() > 20).forEach(System.out::println);
    // StreamTest.Person(name=lisi, age=28)
    // StreamTest.Person(name=wangwu, age=26)
    // StreamTest.Person(name=zhaoliu, age=32)

    // 获取第一个
    personList.stream().limit(1).forEach(System.out::println);
    // StreamTest.Person(name=zhangsan, age=18)

    // 跳过2个
    personList.stream().skip(2).forEach(System.out::println);
    //StreamTest.Person(name=wangwu, age=26)
    //StreamTest.Person(name=zhaoliu, age=32)

    List<Integer> numList = Arrays.asList(1,34,4,23,43,5,32,34,5,56,7,6,6,6);
    // 筛选 > 10  不重复 的 第3,4 个元素
    numList.stream().filter(n -> n > 10).distinct().skip(2).limit(2).forEach(System.out::println);
    // 43,32


    // -------------------映射-------------------
    // 查询所有人员姓名
    String names = personList.stream().map(Person::getName).collect(Collectors.joining(","));
    System.out.println(names);
    // zhangsan,lisi,wangwu,zhaoliu

    List<String> nameList = personList.stream().map(Person::getName).collect(Collectors.toList());
    System.out.println(nameList);
    // [zhangsan, lisi, wangwu, zhaoliu]

    // 查询所有人员名字并转换成大写
    names = personList.stream().map(Person::getName).map(String::toUpperCase).collect(Collectors.joining(","));
    System.out.println(names);
    // ZHANGSAN,LISI,WANGWU,ZHAOLIU


    // ------------------排序--------------------
    personList.stream().sorted((c1,c2)->c1.getAge().compareTo(c2.getAge())).forEach(System.out::println);
    //StreamTest.Person(name=zhangsan, age=18)
    //StreamTest.Person(name=wangwu, age=26)
    //StreamTest.Person(name=lisi, age=28)
    //StreamTest.Person(name=zhaoliu, age=32)

    // 先按照年龄升序,后按照名字降序
    System.out.println("-----------先按照年龄升序,后按照名字降序-----------");
    Comparator<Person> com = (c1,c2) -> c1.getAge().compareTo(c2.getAge());
    com = com.thenComparing((c1,c2) -> c1.getName().compareTo(c2.getName())).reversed();
    personList.add(new Person("zhangsan2", 26));
    personList.stream().sorted(com).forEach(System.out::println);
    //StreamTest.Person(name=zhaoliu, age=32)
    //StreamTest.Person(name=lisi, age=28)
    //StreamTest.Person(name=zhangsan2, age=26)
    //StreamTest.Person(name=wangwu, age=26)
    //StreamTest.Person(name=zhangsan, age=18)

    // 取年龄最小的前两个
    System.out.println("-----------取年龄最小的前两个-----------");
    personList.stream().sorted((c1,c2) -> c1.getAge().compareTo(c2.getAge())).limit(2).forEach(System.out::println);
    //-----------取年龄最小的前两个-----------
    //StreamTest.Person(name=zhangsan, age=18)
    //StreamTest.Person(name=wangwu, age=26)

}

5.4 Stream 终止操作

  • 匹配
  1. allMatch 检查是否匹配所有元素
  2. anyMatch 检查是否至少匹配一个元素
  3. noneMatch 检查是否没有匹配元素
  4. findFirst 返回第一个元素
  5. findAny 返回当前流中的任意元素
  6. count 返回流中总元素个数
  7. max 返回流中最大元素
  8. min 返回流中最小元素

// ---------------终止操作-----------------
// 匹配所有人员年龄都大于 18
boolean allMatch = personList.stream().allMatch(p -> p.getAge() > 18);
System.out.println("所有人员大于18:" + allMatch);
// 所有人员大于18:false

boolean anyMatch = personList.stream().anyMatch(p -> p.getAge() > 18);
System.out.println("任意人员大于18:" + anyMatch);
// 任意人员大于18:true

// 查找年龄最大
Optional<Person> max = personList.stream().max((c1,c2)->c1.getAge().compareTo(c2.getAge()));
System.out.println(max.get());
// StreamTest.Person(name=zhaoliu, age=32)

6. Java8 二元函数接口

6.1 BiPredicate (断言)

源码:

@FunctionalInterface
public interface BiPredicate<T, U> {

    /**
     * Evaluates this predicate on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     * @return {@code true} if the input arguments match the predicate,
     * otherwise {@code false}
     */
    boolean test(T t, U u);
  
    default BiPredicate<T, U> and(BiPredicate<? super T, ? super U> other) {
        Objects.requireNonNull(other);
        return (T t, U u) -> test(t, u) && other.test(t, u);
    }


    default BiPredicate<T, U> negate() {
        return (T t, U u) -> !test(t, u);
    }


    default BiPredicate<T, U> or(BiPredicate<? super T, ? super U> other) {
        Objects.requireNonNull(other);
        return (T t, U u) -> test(t, u) || other.test(t, u);
    }
}

6.2 BiFunction (函数)

源码:

@FunctionalInterface
public interface BiFunction<T, U, R> {

    /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    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));
    }
}

6.3 BiConsumer (消费)

源码:

@FunctionalInterface
public interface BiConsumer<T, U> {

    /**
     * Performs this operation on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     */
    void accept(T t, U u);

    default BiConsumer<T, U> andThen(BiConsumer<? super T, ? super U> after) {
        Objects.requireNonNull(after);

        return (l, r) -> {
            accept(l, r);
            after.accept(l, r);
        };
    }
}

7. 案例

需求:将bean中的属性按照字典key转换成字典name,bean字典属性名称不确定;

7.1 通用Bean 属性字典转换器

定义字典转换

package test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;

import org.junit.Test;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

public class DictTest {
	
	
	private static Map<String,String> dictMap = new HashMap<>();
	static {
		dictMap.put("zhangsan", "张三");
		dictMap.put("lisi", "李四");
		dictMap.put("wangwu", "王五");
		dictMap.put("zhaoliu", "赵六");
	}
	
	
	@Getter
	@Setter
	@ToString
	class Person{
		private String name;
		private Integer age;
		private String chineseName;
		
		public Person(String name, Integer age) {
			this.name = name;
			this.age = age;
		}
	}
	
	/**
	 * 字典转换
	 * @param <T> Bean类型
	 * @param beanList Bean列表
	 * @param getData 获取字典key的方法
	 * @param setData 设置字典value的方法
	 */
	public <T> void dictConvert(List<T> beanList,Function<T, String> getData, BiConsumer<T, String> setData) {
		beanList.forEach(c->{
			String dictValue = dictMap.get(getData.apply(c));
			setData.accept(c, dictValue);
		});
	}
	
	@Test
	public void test01() {
		List<Person> personList = new ArrayList<>();
		personList.add(new Person("zhangsan", 18));
		personList.add(new Person("lisi", 32));
		personList.add(new Person("wangwu", 34));
		personList.add(new Person("zhaoliu", 24));
		
		dictConvert(personList, Person::getName, Person::setChineseName);
		
		personList.forEach(System.out::println);
	}
	
	
	
	@Getter
	@Setter
	@ToString
	class Person2{
		private String nameKey;
		private Integer age;
		private String nameValue;
		
		public Person2(String nameKey, Integer age) {
			this.nameKey = nameKey;
			this.age = age;
		}
	}
	
	@Test
	public void test02() {
		List<Person2> personList = new ArrayList<>();
		personList.add(new Person2("zhangsan", 18));
		personList.add(new Person2("lisi", 32));
		personList.add(new Person2("wangwu", 34));
		personList.add(new Person2("zhaoliu", 24));
		
		List<Person2> personList1 = new ArrayList<>(personList); 
		dictConvert(personList1, Person2::getNameKey, Person2::setNameValue);
		personList1.forEach(System.out::println);
		
		
		List<Person2> personList2 = new ArrayList<>(personList); 
		dictConvert(personList2, c->c.getNameKey(), (c,v)->c.setNameValue(v));
		personList2.forEach(System.out::println);
		// DictTest.Person2(nameKey=zhangsan, age=18, nameValue=张三)
		// DictTest.Person2(nameKey=lisi, age=32, nameValue=李四)
		// DictTest.Person2(nameKey=wangwu, age=34, nameValue=王五)
		// DictTest.Person2(nameKey=zhaoliu, age=24, nameValue=赵六)
	}
}


举报

相关推荐

0 条评论