FunctionalInterface
我们把只定义了单方法的接口称之为FunctionalInterface
,用注解@FunctionalInterface
标记。例如,Callable
接口:
@FunctionalInterface
public interface Callable {
V call() throws Exception;
}
支持函数式编程的都可以使用Lambda
表达式
Lambda表达式
当我们用Arrays.sort()
排序时,可以传入一个Comparator
实例,并采用匿名类的方式来实现:
String[] array = ...
Arrays.sort(array, new Comparator() {
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)
,我们自己定义的方法cmp
和compare
这个方法的方法签名一致,即方法参数和返回类型相同,就可以直接使用方法引用,再看一个例子:
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));
}
查看String
的compareTo
方法发现参数只有一个,但是前面不是说方法签名要一致吗,这又是怎么回事?因为之前的方法是个静态方法,这里是一个实例方法,实例方法第一个隐含参数总是传入this
,相当于:
public static int compareTo(this, String o);
所以String::compareTo
和compare
方法签名是一致的
Stream
一个全新的流失API,可以存储有限或无限个元素,Stream是惰性计算,计算通常时发生在最后结果的获取,因此,Stream API的基本用法就是:创建一个Stream
,然后做若干次转换,最后调用一个求值方法获取真正计算的结果:
如何创建一个Stream?
Stream.of
Stream stream = Stream.of("A", "B", "C", "D");
数组或Collection
Stream stream = Arrays.stream(new String[]{"A", "B", "C"});
Stream stream1 = List.of("A", "B", "C").stream();
Supplier
通过Stream.generate()
需传入Supplier
对象
public static Stream 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 {
/**
* Gets a result.
*
* @return a result
*/
T get();
}
class NatualSupplier implements Supplier {
int n = 0;
@Override
public Integer get() {
return n++;
}
}
基于Supplier
创建的Stream
会不断调用get()
产生下一个元素,可以用来表示无限序列
public static void main(String[] args) {
Stream stream = Stream.generate(new NatualSupplier());
stream.limit(20).forEach(System.out::println);
}
因为它会不断调用get()
,我们必须设定一个界限stream.limit(20)
基本类型
因为Java的泛型不支持基本类型的,只能用Integer
等包装类型,但是Stream
会对频繁的拆箱装箱,所以为了提高效率,Java标准库给我们提供了三种使用基本类型的Stream
-> IntStream
、LongStream
、DoubleStream
map
Stream.map()
是一个转换方法,将一个Stream
转为另一个Stream
Stream stream = List.of(1, 2, 3).stream();
Stream 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.
*
* This is an intermediate
* operation.
*
* @param The element type of the new stream
* @param mapper a non-interfering,
* stateless
* function to apply to each element
* @return the new stream
*/
Stream map(Function super T, ? extends R> mapper);
Stream.map()
传入的是函数式接口Function
,apply()
最终return
计算的结果
@FunctionalInterface
public interface Function {
/**
* 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 stream = List.of(3, 4, 6).stream();
Stream streamMap = stream.filter(item -> item % 2 == 0);
filter
接收Predicate
,test()
过滤掉不满足条件的元素
@FunctionalInterface
public interface Predicate {
/**
* 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 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相当于初始值,见源码注释:
* {@code
* T result = identity;
* for (T element : this stream)
* result = accumulator.apply(result, element)
* return result;
* }
输出Stream
对Stream
做map()
或filter()
操作时是不会进行任何计算的,reduce
会立即得出结果
如何把进行了转换操作的元素保存下来呢?
输出为集合:
collect()
并传入Collectors.toList()
对象:
Stream stream = List.of(3, 4, 6).stream();
List list = stream.map(n -> n * n).collect(Collectors.toList());
输出为数组:
Stream stream = List.of("Apple", "Banana", "Pear").stream();
String[] array = stream.toArray(String[]::new);
输出为Map:
Stream stream = List.of("Apple:Banana", "Pear:Peach").stream();
Map map = stream.collect(Collectors
.toMap(
// 映射为key
s -> s.substring(0, s.indexOf(':')),
// 映射为value
s -> s.substring(s.indexOf(':') + 1)
));
分组输出:
Stream stream = List.of("Apple", "Banana", "Blackberry", "Coconut", "Avocado", "Cherry", "Apricots").stream();
Map> groups = stream.collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()
));
上述代码中Collectors.groupingBy
以元素的首字母为依据做一个分组