一位攻城狮的自我修养

这大概是全网最详尽的Java Stream解析了!深度解析Lam

2022-03-18  本文已影响0人  攻城狮Chova

Lambda表达式

变量作用域

使用示例

匿名内部类

带参函数

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();
});

Collection

forEach

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接口,类型推导已经完成了这些

removeIf

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表达式

replaceAll

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;
});

sort

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

stream和parallStream

Map

forEach

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));

getOrDefault

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"));

putIfAbsent

remove

replace

replaceAll

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

map.merge(key, newMsg, (v1, v2) -> v1 + v2);

compute

map.compute(key, (k, v) -> v == null ? newMsg : v.concat(newMsg));

computeIfAbsent

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");

computeIfPresent

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;

Streams API

forEach

/* 
 * 使用Stream.forEach()进行迭代
 */
 Stream<String> stream = Stream.of("I", "love", "you", "too");
 stream.forEach(str -> System.out.println(str)); 

filter

在这里插入图片描述
/*
 * 保留长度等于3的字符串
 */
 Stream<String> stream = Stream.of("I", "love", "you", "too");
 stream.filter(str -> str.length() == 3)
       .forEach(str -> System.out.println(str));

distinct

在这里插入图片描述
Stream<String> stream = Stream.of("I", "love", "you", "too", "too");
stream.distinct()
      .forEach(str -> System.out.println(str));

sorted

Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.sorted((str1, str2) -> str1.length() - str2.length())
      .forEach(str -> System.out.println(str));

map

在这里插入图片描述
Stream<String> stream = Stream.of("I", "love", "you", "too");
stream.map(str -> str.toUpperCase())
      .forEach(str -> System.out.println(str));

flatMap

在这里插入图片描述
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)); 

Stream API 高级

reduce

/*
 * 求单词长度之和
 */
 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);

collect

/* 
 * 将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));

接口的静态方法和默认方法

在Java 7之前要想在定义好的接口中加入新的抽象方法是很困难甚至不可能的,因为会所有实现了该接口的类都要重新实现.Java 8中的default方法就是用来解决这个问题,直接在接口中实现新加入的方法,引进了default方法之后,可以继续加入static方法来避免专门的工具类

方法引用

Collector

在这里插入图片描述
/*
 * 将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);

使用collect()生成Collection

/*
 * 将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()统计学生的GPA
 */
 Map<Student, Double> studentToGPA = student.stream().collect(Collectors.toMap(Function.identity(), // 如何生成key
                                                                                student -> computeGPA(student)));   // 如何生成value                                                                
/*
 * 将学生成绩分为及格不及格两部分
 */
 Map<Boolean, List<Student>> passingFailing = students.stream().collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));
/*
 * 将员工按照部门进行分组
 */
 Map<Department, List<Employee>> byDept = employees.stream().collect(Collectors.groupingBy(Employee :: getDepartment));
/*
 * 使用下游收集器统计每个部门的人数
 */
 Map<Department, Integer> totalByDept = employees.stream()
                                                 .collect(Collectors.groupingBy(Employee :: getDepartment,
                                                                                Collectors.counting()));

这个groupingBySQL相似,也是高度非结构化

/*
 * 按照部门对员工进行分组,并且只保留员工的名字
 */
 Map<Department, List<String>> byDept = employees.stream()
                                                 .collect(Collectors.groupingBy(Employee :: getDepartment,
                                                  Collectors.mapping(Employee :: getName,
                                                                    Collectors.toList())));

使用collect()做字符串join

/*
 * 使用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 Pipelines

/*
 * ArrayList.forEach()
 */
 public void forEach(Consumer<? super E> action) {
    ...
    for (int i = 0; modCount == expectedModCount && i < size; i ++) {
        // 回调方法
        action.accept(elementData[i]);
    }
    ...
 }
int longestStringLengthStaringWithA = strings.stream().filter(s -> s.startsWith("A"))
                                                      .mapToInt(String :: length)
                                                      .max();
  • Stream上的所有操作分为两类: 因为Stream底层对每一种情况的处理方式不同,所以要进行精细的划分
    • 中间操作: 中间操作只是一种标记
      • 无状态: 指元素的处理不受前面元素的影响,处理完一个元素就能立即知道结果
      • 有状态: 指元素的处理受到别的元素的影响,必须等到所有元素处理之后才能知道结果
    • 结束操作: 只有结束操作才会触发实际的计算
      • 短路操作: 指不用处理全部元素就可以返回结果
      • 非短路操作: 指对所有的元素处理后才可以返回结果

Stream Pipeline实现方案

int longest = 0;
for (String str : strings) {
    if (str.startsWith("A")) {  // 类似filter(),保留以A开头的字符串
        int len = str.length(); // 类似mapToInt(),得到字符串的长度
        longest = Math.max( );
    }
}

Stream Pipeline解决方法

操作如何记录?

操作如何叠加?

方法名 作用
void begin(long size) 开始遍历元素之前调用该方法,通知Sink做好准备
void end() 所有元素遍历完成之后调用,通知Sink没有更多的元素了
boolean cancellationRequested() 是否可以结束操作,可以让短路操作尽早结束
void accept(T t) 遍历元素时调用,接收一个待处理元素并对元素进行处理.<br />Stage将自己包含的操作和回调方法封装到该方法里,<br />前一个Stage只需要调用当前Stage.accept(T t)方法就可以
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);
                }
            };
         }
    };
}
/*
 * 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);
    }
 }

叠加操作如何执行?

在这里插入图片描述
/**
 * 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数据处理结束

执行后的结果位置

// ======================== 错误的收集方式 ========================
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());

总结

上一篇 下一篇

猜你喜欢

热点阅读