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")
评论区