文章目录
函数式编程
从java8开始支持
详细文章见廖雪峰的wiki
1 - Lambda表达式
函数式编程(Functional Programming)是把函数作为基本运算单元,函数可以作为变量,可以接收函数,还可以返回函数。历史上研究函数式编程的理论是Lambda演算,所以我们经常把支持函数式编程的编码风格称为Lambda表达式。
String[] array = ...
Arrays.sort(array, new Comparator<String>() {
public int compare(String s1, String s2) {
return s1.compareTo(s2);
}
});
换成函数式:
String[] array = new String[] { "Apple", "Orange", "Banana", "Lemon" };
Arrays.sort(array, (s1, s2) -> {
return s1.compareTo(s2);
});
1.1 - FunctionalInterface
我们把只定义了单方法的接口称之为
FunctionalInterface
,用注解@FunctionalInterface
标记。
1.2 - 方法引用
方法引用,是指如果某个方法签名和接口恰好一致,就可以直接传入方法引用
public class Main {
public static void main(String[] args){
String[] array = ...;
Array.sort(array,Main::cmp);
}
static int cmp(String s1,String s2){
return s1.compareTo(s2);
}
}
注意:在这里,方法签名只看参数类型和返回类型,不看方法名称,也不看类的继承关系。
1.3 - 构造方法引用
构造方法的引用写法是类名::new
public class Main {
public static void main(String[] args) {
List<String> names = List.of("Bob", "Alice", "Tim");
List<Person> persons = names.stream().map(Person::new).collect(Collectors.toList());
System.out.println(persons);
}
}
class Person {
String name;
public Person(String name) {
this.name = name;
}
public String toString() {
return "Person:" + this.name;
}
}
2 - Stream
-
java.io
中的流是用来处理文件和网络数据的,而Stream
是用来处理任意的java对象实例的 -
Stream API的基本用法:创建一个
Stream
,然后做若干次转换,最后调用一个求值方法获取真正计算的结果
2.1 - 特点
- 它可以“存储”有限个或无限个元素
- 一个
Stream
可以轻易地转换为另一个Stream
,而不是修改原Stream
本身 - 真正的计算通常发生在最后结果的获取,也就是惰性计算
- 惰性计算的特点:
- 一个
Stream
转换为另一个Stream
时,实际上只存储了转换规则,并没有任何计算发生 - 也就是说只存了规则,到需要用的时候才会去计算,并产生内存占用.
- 一个
- 惰性计算的特点:
2.2 - Stream API的特点
- Stream API提供了一套新的流式处理的抽象序列;
- Stream API支持函数式编程和链式操作;
- Stream可以w表示无限序列,并且大多数情况下是惰性求值的
- 无限序列必须先变成有限序列再打印
2.3 - 创建Stream
-
Stream.of()
Stream<String> stream = Stream.of("A", "B", "C", "D");
-
基于数组
Stream<String> stream = Arrays.strem(new String[]{"1","2"})
-
基于Collection
List<String> list = new Array(); Stream<String> stream = list.stream();
-
基于Supplier
Stream<String> s = Stream.generate(Supplier<String> sp);
基于Supplier创建的Stream会不断调用Supplier.get()方法来不断产生下一个元素,这种Stream保存的不是元素,而是算法,它可以用来表示无限序列。
public static void main(String[] args) { Stream<Integer> stream = Stream.generate(new NatureNum()); stream.limit(100).forEach(System.out::println); } static class NatureNum implements Supplier<Integer> { int n = 0; public Integer get() { n++; return n; } }
-
其他方法
- 通过一些API提供的接口,直接获得Stream
- 正则表达式的Pattern对象有一个splitAsStream()方法,可以直接把一个长字符串分割成Stream序列而不是数组
Pattern p = Pattern.compile("\\s+"); Stream<String> s = p.splitAsStream("The quick brown fox jumps over the lazy dog");
-
基本类型
因为java的泛型不支持基本数据类型,只能使用Stream<Integer>
这样的写法,但这样会有频繁装箱拆箱的性能问题,所以java又提供了IntStream
,LongStream
,DoubleStream
,其目的就是为了提高效率.
2.4 - 使用map
Stream.map()
是Stream
最常用的一个转换方法,它把一个Stream
转换为另一个Stream
.可以将一种元素类型转换成另一种元素类型.
示例:
Stream<Integer> s = Stream.of(1, 2, 3, 4, 5);
Stream<Integer> s2 = s.map(n -> n * n);
利用map
可以替换常见的数据格式处理代码:去空格,字符串替换,算法处理等.
2.5 - 使用filter
此方法就是用来过滤掉不符合要求的数据
IntStream.of(1, 2, 3, 4, 5, 6, 7, 8, 9)
.filter(n -> n < 5)
.forEach(System.out::println);
2.6 - 使用reduce
此方法是一个聚合方法,用来将Stream中的数据进行一些操作后合并.
示例:
int sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce(0, (acc, n) -> acc + n);
System.out.println(sum);
其中reduce
中的第一个参数为初始值
没有初始值的情况:
Optional<Integer> opt = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce((acc, n) -> acc + n);
if (opt.isPresent()) {
System.out.println(opt.get());
}
没有初始值的情况时,因为
Stream
的元素有可能是0个,这样就没法调用reduce()
的聚合函数了,因此返回Optional
对象,需要进一步判断结果是否存在。
reduce()
是聚合方法,聚合方法会立刻对Stream
进行计算。
2.7 输出集合
2.7.1 - List
stream.collect(Collectors.toList());
2.7.2 - 数组
String[] array = list.stream().toArray(String[]::new);
2.7.3 - Map
Stream<String> stream = Stream.of("Key1:Value1", "Key2:Value2");
Map<String, String> map = stream
.collect(Collectors.toMap(
// 把元素s映射为key:
s -> s.substring(0, s.indexOf(':')),
// 把元素s映射为value:
s -> s.substring(s.indexOf(':') + 1)));
2.7.4 - 分组输出
list.stream().collect(Collectors.groupingBy(s -> s.substring(0, 1), Collectors.toList()));
分组输出使用Collectors.groupingBy()
,它需要提供两个函数:分组的key和分组的value;
2.8 - 其他操作
2.8.1 - 排序
stream.sorted();
2.8.2 - 去重
stream.distinct();
2.8.3 - 截取
stream.skip(2).limit(3)
2.8.4 - 合并
将2个stream合并为一个
Stream<String> s1 = ...
Stream<String> s2 = ...
// 合并:
Stream<String> s = Stream.concat(s1, s2);
2.8.5 - flatMap
将stream的元素映射出来形成一个新的stream
s.flatMap(list -> list.stream());
2.8.6 - 并行
stream.parallel()
经过parallel()转换后的Stream只要可能,就会对后续操作进行并行处理。我们不需要编写任何多线程代码就可以享受到并行处理带来的执行效率的提升。
2.8.7 - 其他聚合方法
count()
min(Comparator<? super T> cp)
max(Comparator<? super T> cp)
IntStream
、LongStream
和DoubleStream
中的sum()
和average()
boolean allMatch()
测试是否所有元素均满足测试条件boolean anyMatch()
测试是否至少有一个元素满足测试条件forEach()
Stream
提供的常用操作有:转换操作:
map()
,filter()
,sorted()
,distinct()
;合并操作:
concat()
,flatMap()
;并行处理:
parallel()
;聚合操作:
reduce()
,collect()
,count()
,max()
,min()
,sum()
,average()
;其他操作:
allMatch()
,anyMatch()
,forEach()
。