更新时间:2021-07-27 20:58:47
匿名内部类: 匿名内部类仍然是一个类,不需要指定类名,编译器会自动为该类取名
public class MainAnonymousClass {
public static void main(String[] args) {
new Thread(new Runnable(){
@Override
public void run(){
System.out.println("Anonymous Class Thread run()");
}
}).start();;
}
}
public class MainLambda {
public static void main(String[] args) {
new Thread(
() -> System.out.println("Lambda Thread run()")
).start();;
}
}
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, new Comparator<String>() { // 接口名
@Override
public int compare(String s1, String s2) { // 方法名
if(s1 == null)
return -1;
if(s2 == null)
return 1;
return s1.length() - s2.length();
}
});
List<String> list = Arrays.asList("I", "love", "you", "too");
Collections.sort(list, (s1, s2) -> { // 省略参数表类型
if (s1 == null)
return -1;
if (s2 == null)
return 1;
return s1.length() - s2.length();
});
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for(String str : list) {
if (str.length() > 3)
System.out.println(str);
}
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(new Consumer<String>(){
@Override
public void accept(String str) {
if (str.length() > 3) {
System.out.println(str);
}
}
});
// 使用forEach()结合Lambda表达式迭代
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.forEach(str -> {
if (str.length() > 3) {
Systemm.out.println(str);
}
});
上述代码给forEach() 方法传入一个Lambda表达式,不需要知道accept() 方法,也不需要知道Consumer接口,类型推导已经完成了这些
该方法签名: boolean removeIf(Predicate<? super E> filter);
删除容器中所有满足filter指定条件的元素
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Iterator<String> it = list.iterator();
while (it.hasNext()) {
if (it.next().length > 3) {
it.remove();
}
}
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(new Predicate<String>(){
@Override
public boolean test(String str) {
return str.length() > 3;
}
});
Array<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.removeIf(str -> str.length() > 3);
使用Lambda表达式不需要记忆Predicate接口名,也不需要记忆test() 方法名,只需要此处需要一个返回布尔类型的Lambda表达式
该方法签名: void replaceAll(UnaryOperator operator);
对每个元素执行operator指定的操作,并用操作结果来替换原来的元素
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
for (int i = 0; i < list.size(); i ++) {
String str = list.get(i)
if (str.length() > 3) {
list.set(i, str.toUpperCase());
}
}
ArrayList<String> list =new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(new UnaryOperator<>(String){
@Override
public String apply(String str) {
if (str.length() > 3) {
return str.toUpperCase();
}
return str;
}
});
代码调用replaceAll() 方法,并使用匿名内部类实现UnaryOperator接口
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.replaceAll(str -> {
if (str.length > 3) {
return str.toUpperCase();
}
return str;
});
该方法定义在List接口中,方法签名: void sort(Comparator<? super E> c);
根据c指定的比较规则对容器进行排序
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String str1, String str2) {
return str1.length() - str2.length();
}
});
ArrayList<String> list = new ArrayList<>(Arrays.asList("I", "love", "you", "too"));
list.sort((str1, str2) -> str1.length() - str2.length());
该方法签名: Spliterator spliterator();
该方法签名: void forEach(BiConsumer<? super K,? super V> action);
对Map中的每个映射执行action操作
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Map.Entry<Integer, String> entry : map.entrySet()) {
system.out.println(entry.getKey() + "=" + entry.getValue());
}
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach(new BiConsumer<Integer, String>() {
@Override
public void accept(Integer k, String v) {
System.out.println(k + "=" + v);
}
});
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.forEach((k, v) -> System.out.println(k + "=" + v));
该方法签名: V getOrDefault(Object key, V defaultValue);
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
System.out.println(map.getOrDefault(4,"NoValue"));
该方法签名: V putIfAbsent(K key, V value);
该方法签名: remove(Object key);
该方法签名: remove(Object key, Object value);
该方法签名: replace(K key, V value);
该方法签名: replace(K key, V oldValue, V newValue);
该方法签名: replaceAll(BiFunction<? super K, ? super V, ? extends V> function);
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
for (Map.Entry<Integer, String> entry : map.entrySet()) {
entry.setValue(entry.getValue().toUpperCase());
}
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll(new BiFunction<Integer, String, String>(){
@Override
public String apply(Integer k, String v) {
return v.toUpperCase();
}
});
HashMap<Integer, String> map = new HashMap<>();
map.put(1, "one");
map.put(2, "two");
map.put(3, "three");
map.replaceAll(<k, v> -> v.toUpperCase());
该方法签名: merge(K key, V value, BiFunction<? super V, ? super V, ? extends V> remappingFunction);
map.merge(key, newMsg, (v1, v2) -> v1 + v2);
该方法签名: compute(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
map.compute(key, (k, v) -> v == null ? newMsg : v.concat(newMsg));
该方法签名: V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction);
Map<Integer, Set<String>> map = new HashMap<>();
if (map.containsKey(1)) {
map.get(1).add("one");
} else {
Set<String> valueSet = new HashSet<String>();
valueSet.add("one");
map.put(1, valueSet);
}
Map<Integer, Set<String>> map = new HashMap<>();
map.computeIfAbsent(1, v -> new HashSet<String>()).add("one");
该方法签名: V computeIfPresent(K key, BiFunction<? super K, ? super V, ? extends V> remappingFunction);
if (map.get(key) != null) {
V oldValue = map.get(key);
V newValue = remappingFunction.apply(key, oldValue);
if (newValue !=null) {
map.put(key, newValue);
} else {
map.remove(key);
}
return newValue;
}
return null;
stream:
这里的数据源可以是:
stream是一个数据源视图,需要调用对应的工具方法创建一个stream:
图中4种stream接口继承自BaseStream:
为不同的数据类型设置不同的stream接口:
尽管stream是容器调用Collection.stream()方法得到的. stream和collections有以下不同点:
对stream的操作分为两类:
Stream接口常用方法:
中间操作:
结束操作:
区分中间操作和结束操作就是看方法的返回值:
Stream方法使用:
stream与函数接口关系非常紧密,没有函数接口stream就无法操作
该方法签名: void forEach(Consumer<? super E> action);
/*
* 使用Stream.forEach()进行迭代
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.forEach(str -> System.out.println(str));
该函数原型: Stream< T > filter(Predicate<? super T> predicate);
/*
* 保留长度等于3的字符串
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.filter(str -> str.length() == 3)
.forEach(str -> System.out.println(str));
该函数原型: Stream< T > distinct();
Stream<String> stream = Stream.of("I", "love", "you", "too", "too");
stream.distinct()
.forEach(str -> System.out.println(str));
排序函数有两个:
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.sorted((str1, str2) -> str1.length() - str2.length())
.forEach(str -> System.out.println(str));
该函数原型: < R > Stream< R > map(Function<? super T, ? extends R> mapper);
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.map(str -> str.toUpperCase())
.forEach(str -> System.out.println(str));
该函数原型: < R > Stream< R > flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
Stream<List<Integer>> stream = Stream.of(Arrays.asList(1,2), Arrays.asList(3,4,5));
stream.flatMap(list -> list.stream())
.forEach(i -> System.out.println(i));
归约操作: reduction operation
Stream类库中两个通用的规约操作:
reduce()的方法定义有三种重写形式:
从一组单词中找出最长的单词.这里"大"的含义就是"长":
/*
* 找出最长的单词
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
Optional<String> longets = stream.reduce((s1, s2) -> s1.length() >= s2.length() ? s1 : s2);
// Optional<String> longest = stream.max((s1, s2) -> s1.length() - s2.length());
System.out.println(longest.get());
/*
* 求单词长度之和
*/
Stream<String> strean = Stream.of("I", "love", "you", "too");
Integer lengthSum = stream.reduce(0, // 初始值 (1)
(sum, str) -> sum +str.length(), // 累加器 (2)
(a, b) -> a + b); // 部分和拼接,并行执行时会用到 (3)
// int lengthSun = stream.mapToInt(str -> str.length()).sum();
System.out.println(lengthSum);
上述代码 (2) 处的累加器:
/*
* 将Stream转换成容器或者Map
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
List<String> list = stream.collect(Collectors.toList());
Set<String> set = stream.collect(Collectors.toSet());
// 将Stream转换成Map
Map<String, Integer> map = stream.collect(Collectors.toMap(Function.identity(), String::length));
需要注意的有:
Function是一个接口 ,Function.identity() 含义有两个方面:
Java 8允许在接口中加入具体方法. 接口中的具体方法有两种:
在Java 7之前要想在定义好的接口中加入新的抽象方法是很困难甚至不可能的,因为会所有实现了该接口的类都要重新实现.Java 8中的default方法就是用来解决这个问题,直接在接口中实现新加入的方法,引进了default方法之后,可以继续加入static方法来避免专门的工具类
方法引用可以分为四类:
将一个Stream转换成一个容器或者Map至少需要考虑两个方面:
collect() 方法定义: < R > R collect(Supplier< R > supplier, BiConsumer<R, ? super T> accumulator, BiConsumer<R, R> combiner);
/*
* 将Stream规约成List
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
List<String> list1 = stream.collect(ArrayList :: new, ArrayList :: add, ArrayList :: addAll);
System.out.println(list1);
List<String> list2 = stream.collect(Collectors.toList());
System.out.println(list2);
/*
* 将Stream转换成List或者Set
*/
Stream<String> stream = Stream.of("I", "love", "you", "too");
List<String> list = stream.collect(Collectors.toString());
Set<String> set = stream.collect(Collectors.toSet());
/*
* 使用toCollection指定规约容器的类型
*/
ArrayList<String> arrayList = stream.collect(Collectors.toCollection(ArrayList :: new));
HashSet<String> hashSet = stream.collect(Collectors.toCollection(HashSet :: new));
通常在三种情况下collect()的结果会是Map:
使用toMap()生成的收集器:
/*
* 使用toMap()统计学生的GPA
*/
Map<Student, Double> studentToGPA = student.stream().collect(Collectors.toMap(Function.identity(), // 如何生成key
student -> computeGPA(student))); // 如何生成value
使用partitioningBy()生成的收集器:
/*
* 将学生成绩分为及格不及格两部分
*/
Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
使用groupingBy()生成的收集器:
/*
* 将员工按照部门进行分组
*/
Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee :: getDepartment));
有时候仅仅分组是无法满足要求的.在SQL中使用group by是为了方便更高级的查询:
增强版的groupingBy()能够满足这种需求:
/*
* 使用下游收集器统计每个部门的人数
*/
Map<Department, Integer> totalByDept = employees.stream()
.collect(Collectors.groupingBy(Employee :: getDepartment,
Collectors.counting()));
这个groupingBy和SQL相似,也是高度非结构化
下游收集器还可以包含更下游的收集器:
/*
* 按照部门对员工进行分组,并且只保留员工的名字
*/
Map<Department, List<String>> byDept = employees.stream()
.collect(Collectors.groupingBy(Employee :: getDepartment,
Collectors.mapping(Employee :: getName,
Collectors.toList())));
/*
* 使用Collectors.joining()拼接字符串
*/
Stream<String> stream = Stream.of("I", "love", "you");
String joined = stream.collect(Collectors.joining()); // Iloveyou
String joined = stream.collect(Collectors.joining(",")); // I,love,you
String joined = stream.collect(Collectors.joining(",", "{", "}")); // {I,love,you}
通过使用Stream API中引起的疑问:
/*
* ArrayList.forEach()
*/
public void forEach(Consumer<? super E> action) {
...
for (int i = 0; modCount == expectedModCount && i < size; i ++) {
// 回调方法
action.accept(elementData[i]);
}
...
}
Stream API中大量使用Lambda表达式作为回调方法. 但想要理解Stream, 关键的是:
int longestStringLengthStaringWithA = strings.stream().filter(s -> s.startsWith("A"))
.mapToInt(String :: length)
.max();
上述代码用来求出以字母 "A" 开头的字符串的最大长度:
Stream中的相关操作:
中间操作: Intermediate operations
无状态: Stateless
有状态: Stateful
结束操作: Terminal operations
短路操作: short-circuiting
非短路操作:
Stream上的所有操作分为两类: 因为Stream底层对每一种情况的处理方式不同,所以要进行精细的划分
中间操作: 中间操作只是一种标记
- 无状态: 指元素的处理不受前面元素的影响,处理完一个元素就能立即知道结果
- 有状态: 指元素的处理受到别的元素的影响,必须等到所有元素处理之后才能知道结果
结束操作: 只有结束操作才会触发实际的计算
- 短路操作: 指不用处理全部元素就可以返回结果
- 非短路操作: 指对所有的元素处理后才可以返回结果
求最长字符串的长度:
一种直白的实现方式是为每一次函数调用都执行一次迭代,并将处理中间结果发明放到某种数据结构中,比如数组,容器等
这种实现方法实现简单直观,但存在两个明显的缺陷:
int longest = 0;
for (String str : strings) {
if (str.startsWith("A")) { // 类似filter(),保留以A开头的字符串
int len = str.length(); // 类似mapToInt(),得到字符串的长度
longest = Math.max( );
}
}
采用这种方法不但减少了迭代次数,也避免了存储中间结果,这就是Stream Pipeline.将三个操作放在了一次迭代中
关于这个解决方法,可以采用某种方式记录用户每一步的操作,当用户调用结束操作时将之前记录的操作叠加到一起在一次迭代中全部执行完成. 关于这种解决方法,需要解决以下四个问题:
很多Stream的操作会需要一个回调函数 - Lambda表达式,因此一个完整的操作应该是一个三元数组:
方法名 | 作用 |
---|---|
void begin(long size) | 开始遍历元素之前调用该方法,通知Sink做好准备 |
void end() | 所有元素遍历完成之后调用,通知Sink没有更多的元素了 |
boolean cancellationRequested() | 是否可以结束操作,可以让短路操作尽早结束 |
void accept(T t) | 遍历元素时调用,接收一个待处理元素并对元素进行处理. Stage将自己包含的操作和回调方法封装到该方法里, 前一个Stage只需要调用当前Stage.accept(T t)方法就可以 |
对于有状态的操作,Sink的begin() 和end() 方法是必须实现的:
对于短路操作,Sink.cancellationRequest() 是必须实现的:
void accept(U u) {
1. 使用当前Sink包装的回调函数处理u
2. 将处理结果传递给Pipeline下游的Sink
}
/*
* Stream.map(),调用该方法将产生一个新的Stream
*/
public final <R> Stream<R> map(Function<? super P_OUT, ? extends R> mapper) {
...
return new StatelessOp<P_OUT, R>(this, StreamShape.REFERENCE, StreamOpFlag.NOT_SORTED | StreamOpFlag.NOT_DISTINCT) {
@override
/*
* opWripSink()方法返回由回调函数包装成的Sink
*/
Sink<P_OUT> opWrapSink(int flags, Sink<R> downStream) {
return new Sink.ChainedReference<P_OUT, R>(downStream) {
@Override
public void accept(P_OUT u) {
// 使用当前Sink包装的回调函数mapper处理u
R r = mapper.apply(u)
// 将处理结果传递给流水线下游的Sink
downstream.accept(r);
}
};
}
};
}
将回调函数mapper包装到一个Sink中:
示例:
/*
* Stream.sort()中的Sink实现
*/
class RefSortingSink<T> extends AbstractRefSortingSink<T> {
// 存放用于排序的元素
private ArrayList<T> list;
RefSortingSink(Sink<? super T> downstream, Comparator<? super T> comparator) {
super(downstream, comparator);
}
@Override
public void begin(long size) {
...
// 创建一个存放排序元素的列表
list = (size > 0) ? new ArrayList<T>((int)size) : new ArrayList<T>();
}
@Override
public void end() {
// 只有全部元素结束接收之后才能开始排序
list.sort(comparator);
downstream.begin(list.size());
if (!cacellationWasRequested()) {
// 如果下游Sink不包含短路操作,将处理结果传递给流水线下游的Sink
list.forEach(downstream :: accept);
} else {
/*
* 如果下游Sink包含短路操作:
* 每次都调用cancellationRequested()询问是否可以结束处理
*/
for (T t : list) {
if (down.cancellationWasRequested()) {
break;
}
// 将处理结果传递给流水线下游的Sink
downstream.accept();
}
}
downstream.end();
list = null;
}
@Override
public void accept(T t) {
/*
* 使用当前Sink包装动作处理:
* 将元素添加到中间列表中
*/
list.add(t);
}
}
Sink中的四个接口方法的协作方式:
Sink封装了Stream的每一步操作,并使用 [处理 -> 转发] 的模式来叠加操作.一旦调用某个结束操作, 就会触发整个流水线的执行
上游Sink如何找到下游Sink:
在Stream中,设置了一个SinkAbstractPipeline.opWrapSink(int flag, Sink downstream) 方法来得到Sink. 该方法的作用:
使用一个新的Sink对象而不是返回一个Sink字段:
/**
* AbstractPipeline.wrapStack():
* 从下游向上游不断包装Sink,如果最初传入的Sink代表结束操作,函数返回时就可以得到一个代表了流水线上所有操作的Sink
*/
final <P_IN> Sink<P_IN> wrapSink() {
...
for (AbstractPipeline p = AbstractPipeline.this; p.depth > 0; p = p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}
return (Sink<P_IN>) sink;
}
/*
* AbstractPipeline.copyInto():
* 对spliterator代表的数据执行wrappedSink代表的操作
*/
final <P_IN> void copyInto(Sink<P_IN> wrappedSink, Spliterator<P_IN> spliterator) {
...
if (!StreamOpFlag.SHORT_CIRCUIT.isKnown(getStreamAndOpFlags)) {
// 通知开始遍历履历
wrappedSink.begin(spliterator.getExactSizeIfKnown());
// 迭代
spliterator.forEachRemaining(wrappedSink);
// 通知遍历结束
wrappedSink.end();
}
...
}
上述代码首先调用wrappedSink.begin() 方法告诉Sink数据即将到来,然后调用Spliterator迭代器的spliterator.forEachRemaining() 方法对数据进行迭代,最后调用wrappedSink.end() 方法通知Sink数据处理结束
首先不是所有的Stream结束操作都需要返回结果,有些操作只是为了使用副作用Side-effects :
// ======================== 错误的收集方式 ========================
ArrayList<String> results = new ArrayList<>();
stream.filter(s -> pattern.matcher(s).matches()).forEach(s -> results.add(s));
// ======================== 正确的错误收集方式 ====================
List<String> results = stream.filter(s -> pattern.matcher(s).matches()).collect(Collectors.toList());
对于返回数组的情况,结果放在数组中. 但是在最终返回数组之前,结果存储在Node的数据结构中: