首页 > 编程语言 >函数式编程

函数式编程

时间:2022-11-09 16:22:39浏览次数:51  
标签:Stream return 函数 stream 编程 array public String

FunctionalInterface

我们把只定义了单方法的接口称之为FunctionalInterface,用注解@FunctionalInterface标记。例如,Callable接口:

@FunctionalInterface
public interface Callable<V> {
    V call() throws Exception;
}

支持函数式编程的都可以使用Lambda表达式

Lambda表达式

当我们用Arrays.sort()排序时,可以传入一个Comparator实例,并采用匿名类的方式来实现:

String[] array = ...
Arrays.sort(array, new Comparator<String>() {
    public int compare(String s1, String s2) {
        return s1.compareTo(s2);
    }
});

但是这种写法还是挺繁琐的,所以从JDK8开始,我们可以用Lambda表达式来替代这种繁琐的写法:

String[] array = ...
Arrays.sort(array, (s1, s2) -> {
    return s1.compareTo(s2);
});

使用Lambda只需要写出方法的定义,参数类型可以省略,编译器会自动推断出String类型,-> { ... }表示方法体,如果只有一行return代码,还可以更加简洁:

Arrays.sort(array, (s1, s2) -> s1.compareTo(s2));

返回值的类型也是由编译器自动推断的,这里推断出的返回值是int,因此,只要返回int,编译器就不会报错。

方法引用

除了使用Lambda之外,还可以直接传入方法引用:

public static void main(String[] args) {
    String[] array = new String[]{"Apple", "Orange", "Banana", "Lemon"};
    Arrays.sort(array, Main::cmp);
    System.out.println(String.join(", ", array));
}

static int cmp(String s1, String s2) {
    return s1.compareTo(s2);
}

上面的代码是啥意思?可以看到Arrays.sort需传入一个数组和Comparator接口,在Comparator中有个方法int compare(T o1, T o2),我们自己定义的方法cmpcompare这个方法的方法签名一致,即方法参数和返回类型相同,就可以直接使用方法引用,再看一个例子:

public static void main(String[] args) {
    String[] array = new String[]{"Apple", "Orange", "Banana", "Lemon"};
    Arrays.sort(array, String::compareTo);
    System.out.println(String.join(", ", array));
}

查看StringcompareTo方法发现参数只有一个,但是前面不是说方法签名要一致吗,这又是怎么回事?因为之前的方法是个静态方法,这里是一个实例方法,实例方法第一个隐含参数总是传入this,相当于:

public static int compareTo(this, String o);

所以String::compareTocompare方法签名是一致的

Stream

一个全新的流失API,可以存储有限或无限个元素,Stream是惰性计算,计算通常时发生在最后结果的获取,因此,Stream API的基本用法就是:创建一个Stream,然后做若干次转换,最后调用一个求值方法获取真正计算的结果:

如何创建一个Stream?

Stream.of

Stream<String> stream = Stream.of("A", "B", "C", "D");

数组或Collection

Stream<String> stream = Arrays.stream(new String[]{"A", "B", "C"});
Stream<String> stream1 = List.of("A", "B", "C").stream();

Supplier

通过Stream.generate()需传入Supplier对象

public static<T> Stream<T> generate(Supplier<? extends T> s) {
    Objects.requireNonNull(s);
    return StreamSupport.stream(
    new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
}

Supplier是一个函数式接口,我们可以自己实现这个接口

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
class NatualSupplier implements Supplier<Integer> {

    int n = 0;

    @Override
    public Integer get() {
        return n++;
    }
}

基于Supplier创建的Stream会不断调用get()产生下一个元素,可以用来表示无限序列

public static void main(String[] args) {
    Stream<Integer> stream = Stream.generate(new NatualSupplier());
    stream.limit(20).forEach(System.out::println);
}

因为它会不断调用get(),我们必须设定一个界限stream.limit(20)

基本类型

因为Java的泛型不支持基本类型的,只能用Integer等包装类型,但是Stream会对频繁的拆箱装箱,所以为了提高效率,Java标准库给我们提供了三种使用基本类型的Stream  ->  IntStreamLongStreamDoubleStream

map

Stream.map()是一个转换方法,将一个Stream转为另一个Stream

Stream<Integer> stream = List.of(1, 2, 3).stream();
Stream<Integer> streamMap = stream.map(item -> item * item);

我们看看map(),最终会返回一个新结果的Stream

/**
 * Returns a stream consisting of the results of applying the given
 * function to the elements of this stream.
 *
 * <p>This is an <a href="package-summary.html#StreamOps">intermediate
 * operation</a>.
 *
 * @param <R> The element type of the new stream
 * @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,
 *               <a href="package-summary.html#Statelessness">stateless</a>
 *               function to apply to each element
 * @return the new stream
 */
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

Stream.map()传入的是函数式接口Functionapply()最终return计算的结果

@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);
    ...
}

filter

Stream.filter()Stream的另一个常用转换方法

filter即过滤,过滤掉不满足条件的元素,满足条件的构成一个新的Stream

Stream<Integer> stream = List.of(3, 4, 6).stream();
Stream<Integer> streamMap = stream.filter(item -> item % 2 == 0);

filter接收Predicatetest()过滤掉不满足条件的元素

@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);
}

reduce

Stream的一个聚合方法,把一个Stream的所有元素按照聚合函数聚合成一个结果

Stream<Integer> stream = List.of(3, 4, 6).stream();
Integer reduce = stream.reduce(0, (acc, n) -> acc + n);

Stream.reduce()接收BinaryOperator,而它又继承自BiFunction,在BiFunction中有R apply(T t, U u)

/**
 * 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);

所以BinaryOperator实际上是重写了父接口的方法apply(),通过这个方法进行累加计算

第一个参数0相当于初始值,见源码注释:

* 
<pre>{@code
*     T result = identity;
*     for (T element : this stream)
*         result = accumulator.apply(result, element)
*     return result;
* }</pre>

输出Stream

Streammap()filter()操作时是不会进行任何计算的,reduce会立即得出结果

如何把进行了转换操作的元素保存下来呢?

输出为集合:

collect()并传入Collectors.toList()对象:

Stream<Integer> stream = List.of(3, 4, 6).stream();
List<Integer> list = stream.map(n -> n * n).collect(Collectors.toList());

输出为数组:

Stream<String> stream = List.of("Apple", "Banana", "Pear").stream();
String[] array = stream.toArray(String[]::new);

输出为Map:

Stream<String> stream = List.of("Apple:Banana", "Pear:Peach").stream();
Map<String, String> map = stream.collect(Collectors
        .toMap(
                // 映射为key
                s -> s.substring(0, s.indexOf(':')),
                // 映射为value
                s -> s.substring(s.indexOf(':') + 1)
        ));

分组输出:

Stream<String> stream = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots").stream();
Map<String, List<String>> groups = stream.collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()
));

上述代码中Collectors.groupingBy以元素的首字母为依据做一个分组

标签:Stream,return,函数,stream,编程,array,public,String
From: https://www.cnblogs.com/zouycc/p/16874193.html

相关文章

  • Python简单函数--获取当前时间
    importtimedefgetTime():'''%y两位数的年份表示(00-99)%Y四位数的年份表示(000-9999)%m月份(01-12)%d月内中的一天(0-31)%H24小时制小时数(0-23)......
  • Socket编程
    Socket编程1.基本概念1.什么是客户端/服务器架构?服务器就是一系列硬件或软件,为一个或多个客户端(服务的用户)提供所需的“服务”。它存在唯一目的就是等待客户端的请求,并......
  • 写一个函数判断是不是素数
    #include<stdio.h>#include<math.h>intis_prime(intn){ intj=0; for(j=2;j<=sqrt(n);j++) {  if(n%j==0) return0; } //if(j==n); return......
  • js-Date扩展format()函数--处理时间字符串格式
    js-Date扩展format()函数--处理时间字符串格式constformatNumber=n=>{n=n.toString()returnn[1]?n:`0${n}`}处理月份和天的日期字符串,就是个位数前......
  • 递归函数
    楔子在讲今天的内容之前,我们先来讲一个故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山,山里有座庙,庙里有个老和尚讲故事,讲的什么呢?从前有座山......
  • 《Unix/Linux系统编程》第13章学习笔记
    第13章TCP/IP和网络编程13.1TCP/IP协议TCP/IP是互联网的基础,TCP代表传输控制协议,IP代表互联网协议。目前有IPv4(32位地址)和IPv6(128位地址),目前IPv4使用最多。TCP/IP的四......
  • SQL 遇到以零作除数错误——用NULLIF()函数处理为0的分母
      withtb1as(select1a,0b)selectISNULL((a*1.0 /NULLIF(b,0)),0)ABfromtb1 说明:a必须乘以1.0 否则   withtb1 as(select 1a,2b) ......
  • [oeasy]python0012_字符_character_chr函数_根据序号得到字符
    ​ 字符(character)回忆上次内容上次了解了ord函数这个函数可以通过字符得到序号那么可以反过来吗?通过序号得到字符可以吗?​编辑ord的逆运算chr有来就有回......
  • [oeasy]python0012_字符_character_chr函数_根据序号得到字符
    字符(character)回忆上次内容上次了解了ord函数这个函数可以通过字符得到序号那么可以反过来吗?通过序号得到字符可以吗?ord的逆运算chr有来就有回好像可以我们可以把104作......
  • C++ 类继承时的构造函数
    这篇文章主要介绍了C++类继承时的构造函数,C++中,子类继承父类除去构造函数和析构函数以外的所有成员。因此,子类需要编写自己的构造函数和析构函数。更多相关详情需要的小伙......