目 录CONTENT

文章目录

java中函数式编程之高级函数与流

zhouzz
2024-09-15 / 0 评论 / 0 点赞 / 18 阅读 / 19318 字
温馨提示:
本文最后更新于 2024-09-16,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

1.高阶函数

所谓高阶,就是指它是其他函数对象的使用者

作用:

  • 将通用、复杂的逻辑隐含在高阶函数
  • 将易变、未定的逻辑放在外部的函数对象

比如在列表中使用内循环示例:

public class HighFuncTest {

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
        //逆序排列列表,并处理元素
        highOrder(list, value -> System.out.println(value));
    }

    // 内循环
    static <T> void highOrder(List<T> list, Consumer<T> consumer) {
        ListIterator<T> iterator = list.listIterator(list.size());
        //获取上一个元素的值
        while (iterator.hasPrevious()) {
            T value = iterator.previous();
            // 元素处理
            consumer.accept(value);
        }
    }
}

简单流使用:

模仿 Stream,实现自己的 SimpleStream,提供高级函数如 map,filter,forEach

public class SimpleStream<T> {

    private Collection<T> collection;

    public SimpleStream(Collection<T> collection) {
        this.collection = collection;
    }

    public static <T> SimpleStream<T> of(Collection<T> collection) {
        return new SimpleStream<>(collection);
    }

    public SimpleStream<T> filter(Predicate<T> predicate) {
        List<T> results = new ArrayList<>();
        Iterator<T> iterator = collection.iterator();
        while (iterator.hasNext()) {
            T next = iterator.next();
            if (predicate.test(next)) {
                results.add(next);
            }
        }
        return new SimpleStream<>(results);
    }

    // 转换
    public <U> SimpleStream<U> map(Function<T, U> function) {
        List<U> results = new ArrayList<>();
        Iterator<T> iterator = collection.iterator();
        while (iterator.hasNext()) {
            T next = iterator.next();
            U u = function.apply(next);
            results.add(u);
        }
        return new SimpleStream<>(results);
    }

    public void forEach(Consumer<T> consumer) {
        Iterator<T> iterator = collection.iterator();
        while (iterator.hasNext()) {
            T next = iterator.next();
            consumer.accept(next);
        }
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6));
        SimpleStream.of(list)
                .filter(x -> (x & 1) == 1)
                .map(x -> x * x)
                .forEach(System.out::println);
    }
}

简单流-化简

两个元素,按照某种规则合并为一个。

合并规则:

  • 1.两个元素里挑小的
  • 2.两个元素里挑大的
  • 3.两个元素相加

思考: 两个元素操作,需要有返回参数,就联想到用函数接口 BinaryOperator,里面抽象方法R apply(T t, U u);

// 两两元素操作, o 代表初始值
public T reduce(T o, BinaryOperator<T> binaryOperator) {
    // 上次寄存的值
    T p = o;
    for (T t : collection) {
        // 两两操作的值
        p = binaryOperator.apply(p, t);
    }
    return p;
}

使用:

//获取最大值
Integer maxValue = SimpleStream.of(list).reduce(Integer.MIN_VALUE, Integer::max);
System.out.println(maxValue);

//获取最小值
Integer minValue = SimpleStream.of(list).reduce(Integer.MAX_VALUE, Integer::min);
System.out.println(minValue);

//获取所有元素的和
Integer sumValue = SimpleStream.of(list).reduce(0, Integer::sum);
System.out.println(sumValue);

简单流-收集

提供一个新的容器(集合),将元素加入其中

收集规则:

  • 1.用 Set 收集
  • 2.用 StringBuilder 收集
  • 3.用 Map 收集

思考:
1.需要一个容器,不需要返回值。这个可以用 Supplier, 里面抽象方法T get(); 获取 T 类型的容器。
2.往一个容器中收集操作,不用返回值。就联想到用函数接口 BiConsumer, 里面抽象方法void accept(T t, U u); 使用 t这个容器添加元素 u。

//收集, C 代表容器,supplier 用来创造容器
public <C> C collect(Supplier<C> supplier, BiConsumer<C, T> biConsumer) {
    // 创造容器
    C c = supplier.get();
    for (T t : collection) {
        // 容器的收集操作
        biConsumer.accept(c, t);
    }
    return c;
}

使用:

// Set 收集
//SimpleStream.of(list).collect(HashSet::new, (HashSet set, Integer value) -> set.add(value));
Set<Object> results = SimpleStream.of(list).collect(HashSet::new, HashSet::add);
System.out.println(results);

// StringBuilder 收集
StringBuilder results2 = SimpleStream.of(list).collect(StringBuilder::new, StringBuilder::append);
System.out.println(results2);

// StringJoiner 收集
StringJoiner results3 = SimpleStream.of(list)
        .collect(() -> new StringJoiner("-"), (join, num) -> join.add(String.valueOf(num)));
System.out.println(results3);

// 计算元素出现的次数
HashMap<Integer, Integer> results4 = SimpleStream.of(list)
        .collect(HashMap::new, (HashMap<Integer, Integer> map, Integer num) -> {
            if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                Integer value = map.get(num);
                map.put(num, value + 1);
            }
        });
System.out.println(results4);

HashMap<Integer, AtomicInteger> results5 = SimpleStream.of(list)
        .collect(HashMap::new, (map, num) ->
                map.computeIfAbsent(num, k -> new AtomicInteger(0)).incrementAndGet());
System.out.println(results5);

2.Stream 流

Java 8 中,引入了流(Stream)的概念,这个流和以前我们使用的 IO 中的流并不太相同。
所有继承自 Collection 的接口都可以转换为 Stream。还是看一个例子。

假设我们有一个 List 包含 Person 对象,Person 有姓名 name 和年龄 age 连个字段。现要求这个列表中年龄大于 20 的人数。

通常按照以前我们可能会这么写:

long count = 0;
for (Person p : persons) {
	if (p.getAge() > 20) {
		count ++;
	}
}

但如果使用 stream 的话,则会简单很多:

long count = persons.stream().filter(person -> person.getAge() > 20).count();

这只是 Stream 的很简单的一个用法。现在链式调用方法算是一个主流,这样写也更利于阅读和理解编写者的意图,一步方法做一件事。

Stream 的方法分为两类。一类叫惰性求值,一类叫及早求值(终结操作)。

判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream,那么是惰性求值。其实可以这么理解,如果调用惰性求值方法,Stream 只是记录下了这个惰性求值方法的过程,并没有去计算,等到调用及早求值方法后,就连同前面的一系列惰性求值方法顺序进行计算,返回结果。

通用形式为:

Stream.惰性求值.惰性求值. ... .惰性求值.及早求值

整个过程和建造者模式有共通之处。建造者模式使用一系列操作设置属性和配置,最后调用一个 build 方法,这时,对象才被真正创建。

2.1 collect(toList())

collect(toList()) 方法由 Stream 里的值生成一个列表,是一个及早求值操作。可以理解为 Stream 向 Collection 的转换。

注意这边的 toList() 其实是 Collectors.toList(),因为采用了静态倒入,看起来显得简洁。

List<String> list = Stream.of("a", "b", "c").collect(Collectors.toList());
//assertEquals(Arrays.asList("a", "b", "c"), list);
System.out.println(list);

Set<String> set = Stream.of("a", "b", "c").collect(Collectors.toSet());
System.out.println(set);

Map<Long, Student> map = Stream.of(
        new Student(1001L, "张三", 24, "男"),
        new Student(1002L, "李娟", 23, "女"),
        new Student(1003L, "王源", 26, "男"))
        .collect(Collectors.toMap(Student::getId, Function.identity()));
System.out.println(map);

2.2 Map

如果有一个函数可以将一种类型的值转换成另外一种类型,Map 操作就可以使用该函数,将一个流中的值转换成一个新的流。

map 方法就是接受的一个 Function 的匿名函数类,进行的转换。

List<String> list = Stream.of("a", "b", "hello")
        .map(String::toUpperCase).collect(toList());
System.out.println(list);

2.3 filter

遍历数据并检查其中的元素时,可尝试使用 Stream 中提供的新方法 filter。

filter 方法就是接受的一个 Predicate 的匿名函数类,判断对象是否符合条件,符合条件的才保留下来。

List<String> beginningWithNumbers =
Stream.of("a", "1abc", "abc1")
        .filter(value -> Character.isDigit(value.charAt(0)))
        .collect(Collectors.toList());
System.out.println(beginningWithNumbers);

2.4 flatMap

flatMap 方法可用 Stream 替换值,然后将多个 Stream 连接成一个 Stream。

flatMap 最常用的操作就是合并多个 Collection。

List<Integer> together = Stream.of(Arrays.asList(1, 2), Arrays.asList(3, 4))
        .flatMap(numbers -> numbers.stream())
        .collect(Collectors.toList());
System.out.println(together);

2.5 max和min

Stream 上常用的操作之一是求最大值和最小值。Stream API 中的 max 和 min 操作足以解决这一问题。

List<Integer> list = new ArrayList(Arrays.asList(3, 5, 2, 9, 1));
int maxInt = list.stream().max(Integer::compareTo).get();
int minInt = list.stream().min(Integer::compareTo).get();
System.out.println(maxInt);
System.out.println(minInt);

这里有 2 个要点需要注意:

max 和 min 方法返回的是一个 Optional 对象(对了,和 Google Guava 里的 Optional 对象是一样的)。Optional 对象封装的就是实际的值,可能为空,所以保险起见,可以先用 isPresent() 方法判断一下。Optional 的引入就是为了解决方法返回 null 的问题。

Integer::compareTo 也是属于 Java 8 引入的新特性,叫做 方法引用(Method References)。在这边,其实就是 (int1, int2) -> int1.compareTo(int2) 的简写。

2.6 reduce

reduce 操作可以实现从一组值中生成一个值。在上述例子中用到的 count、min 和 max 方法,因为常用而被纳入标准库中。事实上,这些方法都是 reduce 操作。

// 列表中两两数相加获取最终的sum
// reduce 的第一个参数,这是一个初始值。0 + 1 + 2 + 3 + 4 = 10。
int result = Stream.of(1, 2, 3, 4).reduce(0, (acc, element) -> acc + element);
System.out.println(result);

// 列表中两两数比较获取最终的最大值
int max = Stream.of(1, 2, 3, 4).reduce(
  Integer.MIN_VALUE, (acc, element) -> acc >= element ? acc : element);
System.out.println(max);

2.7 构建流

根据已有的数组构建流

Arrays.stream(array)

根据已有的 Collection 构建流(包括 List,Set 等)

List.of("a","b","c").stream()

把一个对象变成流

Stream.of("d")

把多个对象变成流

Stream.of("x", "y")

3.小结

0

评论区