Java Stream流中的实用操作,各种流操作的参考示例分析(1)

  • Stream是对集合Collection对象功能的增强: Stream可以对集合对象进行便利高效的聚合操作和大批量的数据操作 集合类持有的元素都是存储在内存中,数据量很大的集合类会占用大量的内存 .Stream的元素是延迟计算的,只有在访问时才会进入内存进行计算 集合类的迭代逻辑是调用者负责的,通常使用for循环 .Stream中的迭代是隐含在对Stream的各种操作当中

  • Stream操作的两个基础特征: Pipelining: 中间操作都会返回流本身.多个操作可以串成一个管道.这样就可以对操作进行优化,比如延迟执行laziness和短路short-circuiting 内部迭代: 通常对集合的遍历都是通过Iterator或者forEach操作,显式地在集合外部进行迭代 .Stream提供了内部迭代的方式,通过访问者模式Visitor实现

  • Stream的特点: 不存储: 数据流Stream不是存储元素的数据结构 数据流Stream将数据元素从源数据结构,数组,生成器函数和输入输出管道中传递到计算操作的管道中 功能性: 一个数据流Stream中的流操作都会产生一个结果,不会修改源数据流Stream中的元素 延迟计算: 许多流操作中的各种操作,比如过滤,映射,去重等,只有访问时才会进行计算,这样可以有更多的机会进行优化 无界性: 集合的大小是有界的,但是数据流Stream可以是无界的 短路操作等可以允许无限流计算在有限的时间内完成

  • 示例: 将流程转化为Java代码

±-------------------+ ±-----+ ±-----+ ±–+ ±------+

| stream of elements ±----> |filter±> |sorted±> |map±> |collect|

±-------------------+ ±-----+ ±-----+ ±–+ ±------+

List transactionsIds = widgets.stream()

.filter(b -> b.getColor == RED)

.sorted((x, y) -> x.getWeight() - y.getWeight())

.mapToInt(Widget :: getWeight)

.sum();

生成流

===

  • 集合接口有两种方法来生成流:

  • stream(): 为集合创建串行流

  • parallelStream(): 为集合创建并行流 并行流可以充分发挥多核处理器的优势 使用fork和join并行方式来拆分任务和加速处理过程

List strings = Arrays.asList(“I”, “want”, “to”, “be”, “”, “great”);

List filter = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

Stream的创建方式

===========

数组

==

  • 通过数组来生成Stream流

String[] arrayStr = {“a”, “b”, “c”};

// 使用Stream中的静态方法

Stream stream = Stream.of(arrayStr);

集合

==

  • 通过集合来生成Stream流

List list = Arrays.asList(“a”, “b”, “c”);

Stream strem = list.stream();

Stream.generate()

=================

  • 通过Stream.generate()方法来生成Stream流

Stream generate = Stream.generate(() -> 1);

Stream.iterate()

================

  • 通过Stream.iterate()方法来生成Stream流

Stream iterate = Stream.iterate(1, x -> x + 1);

API

===

  • 通过其余相关的API进行创建

String str = “api”;

IntStream stream = str.chars();

Stream常用方法

==========

中间操作

====

  • 中间操作: 如果调用方法之后返回的结果是Stream对象的操作是一个中间操作

filter

======

  • filter方法用于通过设置的条件过滤出元素

/*

  • 过滤出空字符串的个数

*/

List strings = Arrays.asList(“I”, “want”, “to”, “be”, “”, “great”);

long count = Strings.stream().filter(string -> string.isEmpty()).count();

/*

  • 过滤出偶数

*/

Arrays.asList(1, 2, 3, 4, 5).stream()

.filter(x -> x % 2 == 0)

// 2, 4

.forEach(System.out :: println)

/*

  • 求出所有的偶数和

*/

int count = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9).stream()

.filter(x -> x % 2 == 0)

.mapToInt(x -> x)

.sum();

System.out.println(count);

distinct

========

  • distinct方法通过元素的hashcode()和equals()去除重复元素

Arrays.asList(1, 2, 3, 3, 3, 4, 5).stream()

.distinct()

// 1, 2, 3, 4, 5

.forEach(System.out :: println);

集合Set去重:

Arrays.asList(1, 2, 3, 4, 5).stream() .collect(Collectors.toSet()) // 1, 2, 3, 4, 5 .forEach(System.out :: println); 复制代码

sorted

======

  • sorted方法根据自然排序返回Stream流

  • sorted(Comparator comparator)方法根据提供的Comparator进行排序

/*

  • 使用sorted方法实现min求最小值和max求最大值

*/

List list = Arrays.asList(1, 2, 3, 4, 5);

// 数字的自然排序就是从小到大

Optional min = list.stream().sorted().findFirst();

System.out.println(min :: get());

Optional max = list.stream.sorted((a, b) -> b - a).findFirst();

System.out.println(max :: get());

/*

  • 按照字母顺序a - z排序

*/

Arrays.asList(“java”, “python”, “c”).stream().sorted().forEach(System.out :: println);

/*

  • 按照字母的长度进行排序

*/

Arrays.asList(“java”, “python”, “c”).stream().sorted((a, b) -> a.length() - b.length()).forEach(System.out :: println);

limit

=====

  • limit方法用来截取指定长度的Stream流

  • skip方法用来丢弃指定长度的流数据,可以与limit配合实现分页

/*

  • 打印出集合中20 - 30的数据

*/

Stream.iterate(x -> x + 1).limit(50)

// 跳过前20个

.skip(20)

// 输出20 - 30

.forEach(System.out :: println);

map

===

  • map用来将Stream流中的元素按照指定的函数映射成一个新的元素的Stream流

  • flatMap用来将Stream流中的每个元素转换成另一个流,然后将所有流连接成一个新的流

List list = Arrays.asList(“a, b, c”, “1, 2, 3”);

/*

  • 将字符串转换为不带逗号的元素

*/

Stream mapList = list.stream().map(s -> s.replace(“,”, “”));

// abc 123

mapList.forEach(System.out :: println);

Stream flatMapList = list.stream().flatMap(s -> {

// 将每一个元素转换为流数据

String[] arrayStr = s.split(“,”);

Stream stream = Arrays.stream(arrayStr);

return stream;

});

// a b c 1 2 3

flatMapList.forEach(System.out :: println);

peek

====

  • peek方法用来获取Stream流中的每一个元素 类似于map, 但是map中接收的是一个Function表达式,有返回值 peek中接收的是一个Consumer表达式,没有返回值

String str = “11, 22, 33, 44, 55”;

// 11 22 33 44 55 165

System.out.println(Stream.of(str.split(“,”)).peek(System.out :: println).mapToInt(Integer :: valueOf).sum());

终止操作

====

循环-forEach

==========

  • forEach用来循环遍历每一个元素

// 创建一个实体类,包含有一个属性为num属性.并且有一个build()方法用于为num赋值

String str = “1, 2, 3”

/*

  • 输出 num = 1, num = 2, num = 3

*/

Stream.of(str.split(“,”)).map(x -> new User(x)).forEach(System.out :: println);

Stream.of(str.split(“,”)).map(User :: new).forEach(System.out :: println);

Stream.of(str.split(“,”)).map(x -> User.build(x)).forEach(System.out :: println);

Stream.of(str.split(“,”)).map(User :: build).forEach(System.out :: println);

计算-min,max,count,sum

====================

  • min: 返回Stream流元素中的最小值

  • max: 返回Stream流元素中的最大值

  • count: 返回Stream流元素中的总个数

  • sum: 对Stream流元素进行求和

List list = Arrays.asList(1, 2, 3, 4, 5, 6);

// 求集合的最大值 - 6

System.out.println(list.stream().max((a, b) -> a - b).get());

// 求集合的最小值 - 1

System.out.println(list.stream().min((a, b) -> a - b).get());

// 统计元素的个数 - 6

System.out.println(list.stream().count());

// 元素求和

String str = “11, 22, 33, 44, 55”;

System.out.println(Stream.of(str.split(“,”)).mapToInt(x -> Integer.valueOf(x)).sum());

System.out.println(Stream.of(str.split(“,”)).mapToInt(Integer :: valueOf).sum());

System.out.println(Stream.of(str.split(“,”)).map(x -> Integer.valueOf(x)).mapToInt(x -> x).sum());

System.out.println(Stream.of(str.split(“,”)).map(Integer :: valueOf).mapToInt(x -> x).sum());

匹配-anyMatch,allMatch,noneMatch,findFirst,findAny

================================================

  • anyMatch: 接收一个Predicate函数,只要Stream流中有一个元素满足该断言则返回true, 否则返回false

  • allMatch: 接收一个Predicate函数,当Stream流中每一个元素都满足该断言则返回true, 否则返回false

  • noneMatch: 接收一个Predicate函数,当Stream流中每一个元素都不符合该断言则返回true, 否则返回false

  • findFirst: 返回Stream流中的第一个元素

  • findAny: 返回Stream流中的任意一个元素

List list = Arrays.asList(1, 2, 3, 4, 5, 6);

// 如果集合的元素都大于等于0则返回true

System.out.println(list.stream().allMatch(x -> x >= 0));

// 如果集合中有大于5的元素,返回false

Systemm.out.println(list.stream().noneMatch(x -> x > 5));

// 如果集合中有大于3的元素则返回true

System.out.println(list.stream().anyMatch(x -> x > 3));

// 取第一个偶数

System.out.println(list.stream().filter(x -> x % 2 == 0).findFirst().get());

// 取任意一个偶数

System.out.println(list.stream().filter(x -> x % 2 == 0).findAny().get());

收集器-toArray,collect

===================

  • collect: 接收一个Collector实例,将流中元素转变为另外一种数据结构

  • Collector<T, A, R> 是一个接口,包含5个抽象方法: Supplier< A > supplier(): 创建一个结果容器 BiConsumer<A, T> accumulator(): 消费型接口. 第一个参数为容器A, 第二个参数为流中元素T BinaryOperator< A > combiner(): 函数接口. 该函数的作用是将各个子流程的运行结果,即accumulator函数操作后的容器A进行合并 Function<A, R> finisher(): 函数式接口. 参数为容器A, 返回类型为collect方法需要的结果类型R Set< Chracteristics > characteristics(): 返回一个不可变的Set集合,用来表明该Collector的特性

// 将对象值转化为List

List ageList = userList.stream().map(User :: getAge).collect(Collectors.toList());

// 将对象值转化为Set

Set ageSet = userList.stream().map(User :: getAge).collect(Collectors.toMap());

// 将对象值转换为Map. 要求key的取值不能相同

Map<String, Integer> userMap = userList.stream().collect(Colletors.toMap(User :: getName, User :: getAge));

// 字符串分隔符连接

String joinName = userList.stream().map(User :: getName).collect(Collectors.join(“,”, “(”, “)”))

// 聚合操作 - 计算对象总数

Long count = userList.stream().collect(Collectors.counting());

// 聚合操作 - 计算最大值

Integer maxAge = userList.stream().map(User :: getAge).collect(Collectors.maxBy(Integer :: compare)).get();

// 聚合操作 - 计算对象值总数

Integer sumAge = userList.stream().collect(Collectors.summingInt(User :: getAge));

// 聚合操作 - 计算对象值的平均数

Double averageAge = userList.stream().collet(Collectors.averagingDouble(User :: getAge));

// 根据Age分组

Map<Integer, List> ageMap = userList.stream().collect(Collectors.groupingBy(User :: getAge));

// 根据条件分区: 一部分大于10, 一部分小于10

Map<Boolean, List> partMap = userList.stream().collect(Collectors.partitionBy(x -> x.getAge() > 10));

// 规约

Integer value = userList.stream().map(User :: getAge).collect(Collectors.reducing(Integer :: sum)).get();

parallelStream

==============

  • parallelStream是流并行处理程序的代替方法

List strings = Arrays.asList(“I”, “want”, “to”, “be”, “”, “great”);

long count = strings.parallelStream().filter(string -> string.isEmpty()).count();

  • 可以很容易的在顺序运行和并行之间进行直接切换

Collectors

==========

  • Collectors类实现很多归约操作,比如将流转换成集合和聚合元素

  • Collectors可用于返回列表和字符串

List strings = Arrays.asList(“I”, “want”, “to”, “be”, “”, “great”);

// 过滤掉空字符串

List filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

// 合并字符串

String mergedString = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.joining(“,”));

averagingDouble

===============

/**

  • 返回一个Collector,生成应用于输入元素double值函数的算术平均值

  • 结果可能会存在误差

  • @param mapper 获取需要计算算数平均值的值的映射器

  • @return Collector<T, ?, Double> 计算算术平均值的Collector

*/

public static Collector<T, ?, Double> averagingDouble(ToDoubleFunction<? super T> mapper) {

/*

  • 在收集操作的数组中:

  • 索引0 - 运行总和的高位

  • 索引1 - 补偿计算总和的低位

  • 索引2 - 可见值的数量

*/

return new CollectorImpl<>(

() -> new double[4],

(a, t) -> { double val = mapper.applyAsDouble(t); sumWithCompensation(a, val); a[2]++; a[3]+= val;},

(a, b) -> { sumWithCompensation(a, b[0]); sumWithCompensation(a, b[1]); a[2] += b[2]; a[3] += b[3]; return a; },

a -> (a[2] == 0) ? 0.0d : (computeFinalSum(a) / a[2]),

CH_NOID);

}

averagingInt

============

/**

  • 返回一个Collector,生成应用于输入元素int值函数的算术平均值

  • @param mapper 获取需要计算算数平均值的值的映射器

  • @return Collector<T, ?, Double> 计算算术平均值的Collector

*/

public static Collector<T, ?, Double> averagingInt(ToIntFunction<? super T> mapper) {

return new CollectorImpl<>(

() -> new long[2],

(a, t) -> { a[0] += mapper.applyAsInt(t); a[1]++; },

(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },

a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);

}

averagingLong

=============

/**

  • 返回一个Collector,生成应用于输入元素long值函数的算术平均值

  • @param mapper 获取需要计算算数平均值的值的映射器

  • @return Collector<T, ?, Double> 计算算术平均值的Collector

*/

public static Collector<T, ?, Double> averagingLong(ToLongFunction<? super T> mapper) {

return new CollectorImpl<>(

() -> new long[2],

(a, t) -> { a[0] += mapper.applyAsLong(t); a[1]++; },

(a, b) -> { a[0] += b[0]; a[1] += b[1]; return a; },

a -> (a[1] == 0) ? 0.0d : (double) a[0] / a[1], CH_NOID);

}

collectingAndThen

=================

/**

  • 调整Collector进行额外的转换

  • @param downstream 生成的下游收集器

  • @param finisher 额外执行的操作

  • @return Collector<T,A,RR> 进行额外操作的下游收集器

*/

public static<T,A,R,RR> Collector<T,A,RR> collectingAndThen(Collector<T,A,R> downstream, Function<R,RR> finisher) {

Set<Collector.Characteristics> characteristics = downstream.characteristics();

if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) {

if (characteristics.size() == 1)

characteristics = Collectors.CH_NOID;

else {

characteristics = EnumSet.copyOf(characteristics);

characteristics.remove(Collector.Characteristics.IDENTITY_FINISH);

characteristics = Collections.unmodifiableSet(characteristics);

}

}

return new CollectorImpl<>(downstream.supplier(),

downstream.accumulator(),

downstream.combiner(),

downstream.finisher().andThen(finisher),

characteristics);

}

counting

========

/**

  • 返回Collector中指定类型元素的数量

  • @return Collector<T, ?, Long> 指定输入元素数量的收集器

*/

public static Collector<T, ?, Long> counting() {

return summingLong(e -> 1L);

}

groupingBy

==========

/**

  • 返回一个对输入的指定类型的元素执行分组操作的Collector

    • 根据分组函数进行分组操作
    • 并将结果以Map类型返回
  • @param classifier 指定的分组函数

  • @return Collector<T, ?, Map<K, List>> Map类型的返回结果

*/

public static <T, K> Collector<T, ?, Map<K, List>> groupingBy(Function<? super T, ? extends K> classifier) {

return groupingBy(classifier, toList());

}

/**

  • 返回一个对输入的指定类型的元素执行级联分组操作的Collector

    • 根据分组函数进行分组操作
    • 然后根据下游收集器对关联key的value值执行指定的规约操作
  • @param classifier 指定的分组函数

  • @param downstream 指定执行规约操作的下游收集器

  • @return Collector<T, ?, Map<K, D>> 实现了级联分组操作的结果

*/

public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {

return groupingBy(classifier, HashMap::new, downstream);

}

/**

  • 返回一个对输入的指定类型的元素执行级联分组操作的,并以指定的Map实现方式保存结果返回的Collector

    • 根据分组函数进行分组操作
    • 再根据下游收集器对关联key的value值执行指定的规约操作
    • 然后将结果保存到指定方式实现的Map中
  • @param classifier 指定的分组函数

  • @param mapFactory 指定的Map实现

  • @param downstream 指定执行规约操作的下游收集器

  • @return Collector<T, ?, M> 实现了级联分组操作的Collector结果

*/

public static <T, K, D, A, M extends Map<K, D>> Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier, Supplier mapFactory, Collector<? super T, A, D> downstream) {

Supplier downstreamSupplier = downstream.supplier();

BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();

BiConsumer<Map<K, A>, T> accumulator = (m, t) -> {

K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”);

A container = m.computeIfAbsent(key, k -> downstreamSupplier.get());

downstreamAccumulator.accept(container, t);

};

BinaryOperator<Map<K, A>> merger = Collectors.<K, A, Map<K, A>>mapMerger(downstream.combiner());

@SuppressWarnings(“unchecked”)

Supplier<Map<K, A>> mangledFactory = (Supplier<Map<K, A>>) mapFactory;

if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {

return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_ID);

}

else {

@SuppressWarnings(“unchecked”)

Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();

Function<Map<K, A>, M> finisher = intermediate -> {

intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));

@SuppressWarnings(“unchecked”)

M castResult = (M) intermediate;

return castResult;

};

return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_NOID);

}

}

groupingByConcurrent

====================

/**

  • 返回一个对输入的指定类型的元素执行分组操作的并行Collector

    • 根据分组函数进行分组操作
    • 并将结果以Map类型返回
  • @param classifier 指定的分组函数

  • @return Collector<T, ?, ConcurrentMap<K, List>> 实现分组操作的并发无序的Collector

*/

public static <T, K> Collector<T, ?, ConcurrentMap<K, List>> groupingByConcurrent(Function<? super T, ? extends K> classifier) {

return groupingByConcurrent(classifier, ConcurrentHashMap::new, toList());

}

/**

  • 返回一个对输入的指定类型的元素执行级联分组操作的并行Collector

    • 根据分组函数进行分组操作
    • 然后根据下游收集器对关联key的指定value值执行指定的规约操作
  • @param classifier 指定的分组函数

  • @downstream 指定执行规约操作的下游收集器

  • @return Collector<T, ?, ConcurrentMap<K, D>> 实现分组操作的并发无序的Collector

*/

public static <T, K, A, D> Collector<T, ?, ConcurrentMap<K, D>> groupingByConcurrent(Function<? super T, ? extends K> classifier, Collector<? super T, A, D> downstream) {

return groupingByConcurrent(classifier, ConcurrentHashMap::new, downstream);

}

/**

  • 返回一个对输入的指定类型的元素执行级联分组操作的并行Collector

    • 根据分组函数进行分组操作
    • 再根据下游收集器对关联key的指定value值执行指定的规约操作
    • 然后将结果保存到指定方式实现的Map中
  • @param classifier 指定的分组函数

  • @param mapFactory 指定的Map实现

  • @downstream 指定执行规约操作的下游收集器

  • @return Collector<T, ?, ConcurrentMap<K, D>> 实现分组操作的并发无序的Collector

*/

public static <T, K, A, D, M extends ConcurrentMap<K, D>> Collector<T, ?, M> groupingByConcurrent(Function<? super T, ? extends K> classifier, Supplier mapFactory, Collector<? super T, A, D> downstream) {

Supplier downstreamSupplier = downstream.supplier();

BiConsumer<A, ? super T> downstreamAccumulator = downstream.accumulator();

BinaryOperator<ConcurrentMap<K, A>> merger = Collectors.<K, A, ConcurrentMap<K, A>>mapMerger(downstream.combiner());

@SuppressWarnings(“unchecked”)

Supplier<ConcurrentMap<K, A>> mangledFactory = (Supplier<ConcurrentMap<K, A>>) mapFactory;

BiConsumer<ConcurrentMap<K, A>, T> accumulator;

if (downstream.characteristics().contains(Collector.Characteristics.CONCURRENT)) {

accumulator = (m, t) -> {

K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”);

A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());

downstreamAccumulator.accept(resultContainer, t);

};

}

else {

accumulator = (m, t) -> {

K key = Objects.requireNonNull(classifier.apply(t), “element cannot be mapped to a null key”);

A resultContainer = m.computeIfAbsent(key, k -> downstreamSupplier.get());

synchronized (resultContainer) {

downstreamAccumulator.accept(resultContainer, t);

}

};

}

if (downstream.characteristics().contains(Collector.Characteristics.IDENTITY_FINISH)) {

return new CollectorImpl<>(mangledFactory, accumulator, merger, CH_CONCURRENT_ID);

}

else {

@SuppressWarnings(“unchecked”)

Function<A, A> downstreamFinisher = (Function<A, A>) downstream.finisher();

Function<ConcurrentMap<K, A>, M> finisher = intermediate -> {

intermediate.replaceAll((k, v) -> downstreamFinisher.apply(v));

@SuppressWarnings(“unchecked”)

M castResult = (M) intermediate;

return castResult;

};

return new CollectorImpl<>(mangledFactory, accumulator, merger, finisher, CH_CONCURRENT_NOID);

}

}

Java Stream流中的实用操作,各种流操作的参考示例分析

joining

=======

/**

  • 返回一个根据接收元素的顺序将元素连接成一个String字符串的Collector

  • @return Collector<CharSequence, ?, String> 根据接收元素的顺序将元素连接成一个字符串的Collector

*/

public static Collector<CharSequence, ?, String> joining() {

return new CollectorImpl<CharSequence, StringBuilder, String>(

StringBuilder::new, StringBuilder::append,

(r1, r2) -> { r1.append(r2); return r1; },

StringBuilder::toString, CH_NOID);

}

/**

  • 返回一个根据接收元素的顺序将元素连接成一个String字符串并以指定的分隔符分割的Collector

  • @param delimiter 用于每个元素之间的分割符

  • @return Collector<CharSequence, ?, String> 根据接收元素的顺序将元素连接成一个字符串并以指定分割符分割的Collector

*/

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter) {

return joining(delimiter, “”, “”);

}

/**

  • 返回一个根据接收元素的顺序将元素连接成一个String字符串并以指定的分隔符分割和指定前缀和后缀的Collector

  • @param delimiter 用于每个元素之间的分割符

  • @param prefix 指定的前缀

  • @param suffix 指定的后缀

  • @return Collector<CharSequence, ?, String> 根据接收元素的顺序将元素连接成一个字符串并以指定分割符分割和指定的前缀和后缀的Collector

*/

public static Collector<CharSequence, ?, String> joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix) {

return new CollectorImpl<>(

() -> new StringJoiner(delimiter, prefix, suffix),

StringJoiner::add, StringJoiner::merge,

StringJoiner::toString, CH_NOID);

}

mapping

=======

/**

  • 在累积元素之前应用映射函数将类型为U的收集器调整为类型为T的收集器

  • mapping操作对多级规约操作最有用

  • @param mapper 映射函数

  • @param downstream 下游收集器

  • @return Collector<T, ?, R> 执行映射函数操作后的收集器

*/

public static <T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream) {

BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();

return new CollectorImpl<>(downstream.supplier(),

(r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),

downstream.combiner(), downstream.finisher(),

downstream.characteristics());

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
img

最后的话

无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!

部分截图:
在这里插入图片描述

操作后的收集器

*/

public static <T, U, A, R> Collector<T, ?, R> mapping(Function<? super T, ? extends U> mapper, Collector<? super U, A, R> downstream) {

BiConsumer<A, ? super U> downstreamAccumulator = downstream.accumulator();

return new CollectorImpl<>(downstream.supplier(),

(r, t) -> downstreamAccumulator.accept(r, mapper.apply(t)),

downstream.combiner(), downstream.finisher(),

downstream.characteristics());

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-RxS8i9CZ-1711947300787)]
[外链图片转存中…(img-YwD13Mit-1711947300788)]
[外链图片转存中…(img-c8zmzQRG-1711947300788)]
[外链图片转存中…(img-dWTaFPCj-1711947300788)]
[外链图片转存中…(img-lbgeUN1b-1711947300789)]
[外链图片转存中…(img-4fICyCwx-1711947300789)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-Y99Vz4sZ-1711947300789)]

最后的话

无论是哪家公司,都很重视Spring框架技术,重视基础,所以千万别小看任何知识。面试是一个双向选择的过程,不要抱着畏惧的心态去面试,不利于自己的发挥。
同时看中的应该不止薪资,还要看你是不是真的喜欢这家公司,好了希望这篇文章对大家有帮助!

部分截图:
[外链图片转存中…(img-BpWOfTmB-1711947300790)]

  • 29
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值