函数式编程
- 将函数作为基本运算单元
- 函数可以作为变量
- 函数可以接收函数,可以返回函数
- 研究函数式编程的理论是λ演算,所以将函数式编程的风格称为Lambda表达式
Arrays.sort(array, (s1,s2)->{ // 参数
return s1.compareTo(s2); // 函数体
})
- 如上所示,可以简化语法(简化单抽象方法实现),类型自动推断
- 只定义了单个抽象方法的接口可以被标注为
@FunctionalInterface
,单抽象方法接口也被称为函数式接口 - 即函数式编程涉及到的接口均是这类函数式接口,例如
interface Comparator<>
方法引用
- 如果某个方法签名(参数类型和返回值)和接口恰好一致,就可以直接传入方法引用
- 引用格式:
类名::方法名
- 静态方法引用:
实现Comparator接口的类的对象的列表或数组可以用Array.sort()方法排序,这里的排序规则引用了自定义的静态方法
- 实例方法引用:
实例方法的调用必须传入一个隐藏的实例变量,例如这里的instance.compareToIgnoreCase(s)
就是说我们实例化的方法都存在一个隐藏的this变量,也是经常使用的
- 构造方法引用
.map()需要传入一个返回值为Person的方法,Person的构造方法返回this,正好符合
- 可以认为这里所说的接口就是被操作对象(数据)所使用的接口或者类
import java.util.Arrays;
class SortedBy {
static int name(String s1, String s2) {
return s1.compareTo(s2);
}
static int nameIgnoreCase(String s1, String s2) {
return s1.toLowerCase().compareTo(s2.toLowerCase());
}
static int length(String s1, String s2) {
int n1 = s1.length();
int n2 = s2.length();
if (n1 == n2) {
return s1.compareTo(s2);
}
return n1 < n2 ? -1 : 1;
}
}
public class LambdaSort {
public static void main(String[] args) throws Exception {
String[] array = "Java Apple lambda functional OOP".split(" ");
// 静态方法引用,方法签名正好符合Comparator接口
Arrays.sort(array, SortedBy::name); // SortedBy::nameIgnoreCase
System.out.println(Arrays.toString(array));
}
}
Stream
- Java8引入的API,定义在
java.util.stream
- 对比:
- 特点
- 可以存储有限个或无限个元素,这里的存储可以存在内存中或者实时计算产生
- 可以转换为另一个stream
- 惰性计算(通常发生在最后结果的获取)
- 创建stream
- 通过指定元素/现有数组/现有Collection创建
// 指定元素:Stream.of(...)
// 现有数组:
import java.util.Arrays;
public class StreamBasic {
public static void main(String[] args) throws Exception {
String[] array = "JDK Stream API supports functional-style operations".split(" ");
// array -> stream:
long n = Arrays.stream(array) // 创建固定大小的stream
// .filter((s) -> s.equals(s.toUpperCase())) // 纯大写字符串
.count();
System.out.println("How many words? " + n);
}
}
// 现有Collection:collection.stream()
- 通过传入Supplier创建无限序列
Stream<T> s = Stream.generate(Supplier<T> s); // 通过传入supplier对象不断产生内容
// 这种对象保存的是算法
- 通过其他类的相关方法创建(Stream API)
由于不能使用Stream<int> s这样的方式创建基本类型的stream,java提供了以下类型:
Stream.map
- 将一个stream转换为另一个,两个是stream按照映射函数一一对应
import java.util.Arrays;
import java.util.stream.Stream;
public class StreamMapSample {
public static void main(String[] args) throws Exception {
String[] array = "Stream API supports functional-style operations".split(" ");
Stream<String> stream = Arrays.stream(array);
stream.map(String::toUpperCase).forEach(System.out::println);// 方法引用作为映射函数
}
}
import java.util.Arrays;
import java.util.stream.Stream;
class Person {
String name;
char gender;
public Person(String name, char gender) {
this.name = name;
this.gender = gender;
}
public String toString() {
return "Person(" + name + ", " + gender + ")";
}
}
public class StreamMapSample2 {
public static void main(String[] args) throws Exception {
String[] inputs = { "Bob,M", "Alice,F", "Time,M", "Lily,F" };
Stream<String> names = Arrays.stream(inputs);
Stream<Person> persons = names.map((s) -> {// 可以将一种元素类型转化为另一种元素类型
int n = s.indexOf(',');
String name = s.substring(0, n);
char gender = s.charAt(n + 1);
return new Person(name, gender);
});
persons.forEach(System.out::println);
}
}
Stream.fliter
- 排除不满足条件的元素
import java.util.function.Supplier;
import java.util.stream.Stream;
class NaturalSupplier implements Supplier<Long> {
long x = 0;
public Long get() { // 默认调用get方法
x++;
return x;
}
}
public class StreamFilterSample {
public static void main(String[] args) throws Exception {
Stream<Long> natural = Stream.generate(new NaturalSupplier());// 传入算法,得到无限序列
Stream<Long> odd = natural.filter((n) -> n % 2 == 1);
odd.limit(20).forEach(System.out::println); // 限制只产生20个
}
}
Stream.reduce
- stream的聚合方法,把一个stream的所有元素聚合成一个结果
- acc和n分别被前两个元素初始化
- acc是上一次计算的结果,n是stream中下一个值
import java.util.stream.Stream;
public class StreamReduceSample {
public static void main(String[] args) throws Exception {
int r = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9).reduce((acc, x) -> acc * x).get();
System.out.println(r);
}
}
import java.util.Arrays;
public class StreamReduceSample2 {
public static void main(String[] args) throws Exception {
String[] array = "Stream API supports functional-style operations".split(" ");
String result = Arrays.stream(array).map(String::toLowerCase).reduce((acc, s) -> acc + " ~ " + s).get();
System.out.println(result);
}
}
- reduce方法会立刻对stream进行计算
其他常用操作
结语
对java基础的学习到这里告一段落,可以体会到Java生态的庞大,我们可以借助它轻松地完成很多工作,Java解决问题的思路有很多值得学习的地方,需要在实践中多加积累!
后面会学习记录一些Java较深层次的原理,路漫漫其修远兮…