Java8 Stream 流式编程

一.Lambda表达式

Lambda表达式也可以称为闭包,它是推动Java8发布的最重要新特性,lambda允许把函数作为一个方法参数传递给方法。

在Java8之前,如果我们新创建一个线程对象,需要使用匿名内部类传递我们要执行的任务,在Java8我们可以使用lambda简化这种操;

public static void main(String[] args) {
// 匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("使用匿名内部类1");
}
}).start(); // lambda表达式
new Thread(() -> {
System.out.println("使用匿名内部类2");
}).start();
}

什么是函数式接口呢?它必须满足如下条件:

  1. 函数式接口只能包含一个方法
  2. 可以包含多个默认方法default(默认方法相当于已经实现的方法,默认方法不会影响lambda表达式对接口方法的实现)
  3. Object类下的方法不计算在内,例如:toString()、equals()、hashCode()等方法。

限制接口类只有一个抽象方法:@FunctionalInterface注解

例如 Runnble

TreeMap 的Comparator

二. 方法引用

我们可以将lambda表达式的实现逻辑封装成一个方法,然后直接在lambda表达式函数中调用封装好的方法,称为方法引用,方法引用包括静态方法引用和动态方法引用

无返回值的

public class TestFunction {

    public static void main(String[] args) {
// 静态方法引用
print(TestFunction::format1);
// 普通方法引用
print(new TestFunction()::format2);
} public static void format1(String name, int age) {
System.out.printf("name: %s, age: %s%n", name, age);
} public void format2(String name, int age) {
System.out.printf("name: %s, age: %s%n", name, age);
} public static void print(PrintFunction function) {
function.print("王大", 23);
}
}

有返回值的

public class TestResultFunction {

    public static void main(String[] args) {
// 静态方法引用
String nameAndAge1 = getNameAndAge("张三", 18, TestResultFunction::format1); // 普通方法引用
String nameAndAge2 = getNameAndAge("张三", 18, new TestResultFunction()::format2);
// 使用函数调用
ResultFunction resultFunction = (name, age) -> {
return name + ":" + age;
}; String nameAndAge3 = resultFunction.getNameAndAge("张三", 18); System.out.println(nameAndAge1);
System.out.println(nameAndAge2);
System.out.println(nameAndAge3);
} public static String format1(String name, int age) {
return name + ":" + age;
} public String format2(String name, int age) {
return name + ":" + age;
} public static String getNameAndAge(String name, Integer age, ResultFunction function) {
return function.getNameAndAge(name, age);
}
}

三. 四大内置核心函数式接口

因为我们不可能每次需要用到函数式接口就去定义一个接口,这样就是重复工作,所以java给我们按照需求的类型(消费型,供给型,函数型,断言型)提供了四个规范接口,以及他们的拓展变种接口;

1. 消费型接口

无返回值,只处理数据;例如 Stream.peek; forEach; Optional.ifPresent

Consumer<T>
void accept(T t);

2. 供给型接口

没有参数,只返回数据,例如 Optional.orElseGet; Optional.orElseThrow;

Supplier<T>
T get();

例如给缓存方法提供为空的值

public class CacheUtil {

    private static HashMap<String, Object> localCache = new ConcurrentHashMap<>();

    public <T> T get(String key, RedisSupplier<T> redisSupplier) {
Object value = localCache.get(key);
if (Objects.isNull(value)) {
T result = redisSupplier.get();
this.set(key, result, redisSupplier.getExpire(), redisSupplier.getTimeUnit());
return result;
}
return (T) value;
}
}

3. 函数型接口

提供参数加获取返回值,例如Stream.map; Optional.map; Map.compute; Stream.mapToInt; MybatisPlus.select; MybatisPlus.eq;

Function <T, R>
R apply(T t);

4. 断言型接口

返回boolean类型值; 例如Stream.filter; Stream.anyMatch; Stream.allMatch;Optional.filter

Predicate<T>
boolean test(T t);

四.Stream流提供的常用函数

Stream提供的方法分为两种,中间处理数据的方法,和结果集收集方法;

Stream流特性:

  1. 不存储数据
  2. 不改变源数据
  3. 不可重复使用

中间处理方法

函数 解释
map 数据处理,返回新的数据流
flatMap 数据维度降级(合并列表数据)
filter 过滤数据
peek 查看数据
distinct 去重
sorted 排序
limit 数据截取,默认从第一个开始
skip 跳过N个数据

终端收集方法

函数 解释
forEach 数据处理,返回新的数据流
max/min/count 最大值/最小值/计数
reduce 归约函数
anyMatch 至少匹配一个元素
allMatch 匹配所有元素
noneMatch 没有匹配到的所有元素
findFirst 在此流中查找第一个元素
findAny 在此流中查找任意一个元素,存在随机性,一般也是第一个,主要是在并行流中体现
toArray 转成数组
collect 收集器,将流转换为其他形式

collect收集方法

函数 解释
toList 将流中的元素收集到一个List中
toSet 将流中的元素收集到一个Set中
toCollection 将流中的元素收集到一个Collection中
toMap 将流中的元素映射收集到一个Map中
counting 统计流中的元素个数
summingInt 计算流中指定int字段的累加总和。针对不同类型的数字类型,有不同的方法,比如summingDouble等
averagingInt 计算流中指定int字段的平均值。针对不同类型的数字类型,有不同的方法,比如averagingLong等
joining 将流中所有元素(或者元素的指定字段)字符串值进行拼接,可以指定拼接连接符,或者首尾拼接字符
maxBy 根据给定的比较器,选择出值最大的元素
minBy 根据给定的比较器,选择出值最小的元素
groupingBy 根据给定的分组函数的值进行分组,输出一个Map对象
partitioningBy 根据给定的分区函数的值进行分区,输出一个Map对象,且key始终为布尔值类型
collectingAndThen 包裹另一个收集器,对其结果进行二次加工转换
reducing 从给定的初始值开始,将元素进行逐个的处理,最终将所有元素计算为最终的1个值输出

高级: 自定义Collector收集器,实现Collector接口

中间处理方法使用

map的使用

map函数的作用是遍历Collection中的元素,生成一个新的Collection

public class TestStream {

    @Test
public void testMap() {
UserInfo userInfo1 = new UserInfo("张三",18,"18273416040");
UserInfo userInfo2 = new UserInfo("李四",20,"18273416040");
UserInfo userInfo3 = new UserInfo("王五",17,"18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); System.out.println("*************抽取对象集合中的某个字段 返回数组***************");
String[] usernameArrays = userInfos.stream().map(UserInfo::getUsername).toArray(String[]::new);
System.out.println(Arrays.toString(usernameArrays)); System.out.println("*************抽取对象集合中的某个字段 返回集合***************");
List<String> usernameList = userInfos.stream().map(UserInfo::getUsername).collect(Collectors.toList());
System.out.println(usernameList); System.out.println("*************对象属性修改***************");
List<UserInfo> updateList = userInfos.stream().map(item -> {
item.setAge(100);
item.setMobile("123");
return item;
}).collect(Collectors.toList());
System.out.println(updateList); System.out.println("*************对象集合转map集合***************");
List<Map<String, Object>> mapList = userInfos.stream().map(BeanUtil::beanToMap).collect(Collectors.toList());
System.out.println(mapList); System.out.println("*************map集合转对象集合***************");
List<UserInfo> mapToBeamList = mapList.stream().map(item -> {
UserInfo userInfo = new UserInfo();
userInfo.setUsername(item.get("username").toString());
userInfo.setMobile(item.get("mobile").toString());
userInfo.setAge((Integer) item.get("age"));
return userInfo;
}).collect(Collectors.toList()); System.out.println(mapToBeamList);
}
}

FlatMap使用

flatmap用于集合的维度降级,也可以理解成把多个Stream流合成一个流;比如多维数组,集合中的元素中包含集合;

@Test
public void testFlatMap() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040"); List<String> addressList1 = new ArrayList<>();
addressList1.add("北京市海淀区");
addressList1.add("广州市天河区");
userInfo1.setAddress(addressList1); List<String> addressList2 = new ArrayList<>();
addressList2.add("广州市天河区");
addressList2.add("广州市海珠区");
userInfo2.setAddress(addressList2); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2);
System.out.println(userInfos); List<List<String>> collect1 = userInfos.stream().map(UserInfo::getAddress).collect(Collectors.toList());
// 也可以distinct
Set<String> collect2 = userInfos.stream().map(UserInfo::getAddress).flatMap(Collection::stream).
collect(Collectors.toSet()); System.out.println(collect1);
System.out.println(collect2);
}

filter peek distinct sorted 使用

public class TestStream {

    @Test
public void testPage() {
String[] names = {"宋江", "卢俊义", "吴用", "公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进"};
String[] mobiles = {"10086", "10010"};
List<UserInfo> userInfos = new ArrayList<>();
for (int i = 0; i < 10; i++) {
UserInfo userInfo = new UserInfo(names[i], i + 17, mobiles[i % 2]);
userInfos.add(userInfo);
}
userInfos.forEach(System.out::println);
System.out.println("\n**************************** filter ******************************\n"); // filter
List<UserInfo> filters = userInfos.stream().filter(item -> item.getAge() > 24).collect(Collectors.toList());
System.out.println(filters); System.out.println("\n***************************** peek ***************************\n"); List<UserInfo> peeks1 = userInfos.stream().peek(System.out::println).collect(Collectors.toList());
// List<UserInfo> peeks2 = userInfos.stream().peek(item -> item.setAge(0)).collect(Collectors.toList());
// System.out.println(peeks2); System.out.println("\n***************************** distinct ***************************\n");
List<String> mobileList1 = userInfos.stream().map(UserInfo::getMobile).collect(Collectors.toList());
List<String> mobileList2 = userInfos.stream().map(UserInfo::getMobile).distinct().collect(Collectors.toList());
System.out.println(mobileList1);
System.out.println(mobileList2); System.out.println("\n***************************** sorted ***************************\n");
// 自然顺序对流的元素进行排序。元素类必须实现Comparable接口
List<UserInfo> sorted1 = userInfos.stream().sorted().collect(Collectors.toList());
sorted1.forEach(System.out::println); // reverseOrder降序 naturalOrder升序
List<UserInfo> sorted2 = userInfos.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
List<UserInfo> sorted3 = userInfos.stream().sorted(Comparator.naturalOrder()).collect(Collectors.toList());
System.out.println("sorted2");
sorted2.forEach(System.out::println);
System.out.println("sorted3");
sorted3.forEach(System.out::println); List<UserInfo> sorted4 = userInfos.stream().sorted(Comparator.comparing(UserInfo::getAge).reversed()).collect(Collectors.toList());
System.out.println("sorted4");
sorted4.forEach(System.out::println); List<UserInfo> sorted5 = userInfos.stream().sorted(Comparator.comparingInt(UserInfo::getAge).reversed()).collect(Collectors.toList()); List<UserInfo> sorted6 = userInfos.stream().sorted((e1, e2) -> {
if (Objects.equals(e2.getAge(), e1.getAge())) {
return e1.getUsername().compareTo(e2.getUsername());
}
return Integer.compare(e2.getAge(), e1.getAge());
}).collect(Collectors.toList()); System.out.println("\n***************************** limit ***************************\n");
List<UserInfo> limit1 = userInfos.stream().limit(1).collect(Collectors.toList());
List<UserInfo> limit2 = userInfos.stream().sorted(Comparator.comparing(UserInfo::getAge).reversed())
.limit(1).collect(Collectors.toList()); System.out.println(limit1);
System.out.println(limit2); }
}

skip limit

limit方法,它是用于限制流中元素的个数,即取前n个元素,返回新的流;

skip()方法用于跳过前面n个元素,然后再返回新的流;

public class TestStream {

    @Test
public void testSkipAndLimit() {
String[] names = {"宋江", "卢俊义", "吴用", "公孙胜", "关胜", "林冲", "秦明", "呼延灼", "花荣", "柴进"};
String[] mobiles = {"10086", "10010"};
List<UserInfo> userInfos = new ArrayList<>();
for (int i = 0; i < 10; i++) {
UserInfo userInfo = new UserInfo(names[i], i + 17, mobiles[i % 2]);
userInfos.add(userInfo);
}
List<UserInfo> skip = userInfos.stream().skip(2).collect(Collectors.toList());
List<UserInfo> limit = userInfos.stream().limit(2).collect(Collectors.toList());
System.out.println("skip");
System.out.println(skip);
System.out.println("limit");
System.out.println(limit); System.out.println("\n*****************************skip加limit 实现分页**********************\n");
long pageSize = 3; long totalPage = 4; for (int pageIndex = 1; pageIndex <= totalPage; pageIndex++) {
List<UserInfo> infoList = userInfos.stream().skip((pageIndex - 1) * pageSize).limit(pageSize).collect(Collectors.toList());
System.out.println(infoList);
System.out.println();
}
}
}

终端收集方法 使用

forEach

public class TestStreamCollection {

    @Test
public void testForEach() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3);
userInfos.stream().forEach(item -> System.out.println(item));
}
}

count

public class TestStreamCollection {
@Test
public void testCount() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); long count = userInfos.stream().count();
// Long collect = userInfos.stream().collect(Collectors.counting()); UserInfo maxUser = userInfos.stream().max(Comparator.comparing(UserInfo::getAge)).get();
// UserInfo maxUser = userInfos.stream().max(Comparator.comparingInt(UserInfo::getAge)).get();
// UserInfo maxUser = userInfos.stream().max((o1,o2)->o1.getAge()-o2.getAge()).get(); UserInfo minUser = userInfos.stream().min(Comparator.comparing(UserInfo::getAge)).get(); System.out.println("count:" + count);
System.out.println("maxUser:" + maxUser);
System.out.println("minUser:" + minUser);
}
}

reduce

public class TestStreamCollection {

    @Test
public void testReduce() {
List<Integer> ids = Stream.of(1, 2, 3, 4).collect(Collectors.toList());
// 两个参数,第一个参数是上次函数执行的返回值(也称为中间结果),第二个参数是stream中的元素,
// 这个函数把这两个值相加,得到的和会被赋值给下次执行这个函数的第一个参数
//第一次执行的时候第一个参数的值是Stream的第一个元素,第二个参数是Stream的第二个元素
Optional<Integer> reduce = ids.stream().reduce((acc, item) -> {
acc += item;
return acc;
}); reduce.ifPresent(System.out::println); // 从而第一次执行的时候第一个参数的值是初始值,第二个参数是Stream的第一个元素,因为开始值是已经存在的,不存在null的情况,所以返回值是确定的类型
Integer reduce1 = ids.stream().reduce(2, (acc, item) -> {
acc += item;
return acc;
});
System.out.println(reduce1); //返回与集合中元素不同类型的值,方便我们对复杂对象做计算式和转换
// 一个参数和两个参数的reduce()只能返回与集合中元素同类型的值。
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3);
Integer sum = userInfos.stream().parallel().reduce(0,
new BiFunction<Integer, UserInfo, Integer>() {
@Override
public Integer apply(Integer integer, UserInfo userInfo) {
return integer + userInfo.getAge();
}
}, new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer integer, Integer integer2) {
System.out.println("integer1:" + integer + "--integer2:" + integer2);
return integer + integer2;
}
}); System.out.println(sum);
}
}

match

public class TestStreamCollection {
@Test
public void testMatch() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); boolean anyMatch = userInfos.stream().anyMatch(item -> item.getAge() > 18);
boolean allMatch = userInfos.stream().anyMatch(item -> item.getAge() > 17);
boolean noneMatch = userInfos.stream().noneMatch(item -> item.getAge() > 18); System.out.println(anyMatch);
System.out.println(allMatch);
System.out.println(noneMatch);
}
}

find

public class TestStreamCollection {

    @Test
public void testFind() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); Optional<UserInfo> first = userInfos.stream().filter(item -> item.getAge() > 18).findFirst(); Optional<UserInfo> any = userInfos.stream().findAny(); first.ifPresent(System.out::println);
any.ifPresent(System.out::println);
} }

toArray

public class TestStreamCollection {

    @Test
public void testToArray() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); UserInfo[] userInfos1 = userInfos.stream().toArray(UserInfo[]::new);
System.out.println(Arrays.toString(userInfos1));
} }

Collect

public class TestStreamCollection {

    @Test
public void testCollect() {
UserInfo userInfo1 = new UserInfo("张三", 18, "18273416040", "人力资源部");
UserInfo userInfo2 = new UserInfo("李四", 20, "18273416040", "软件研发部");
UserInfo userInfo3 = new UserInfo("王五", 17, "18273416040", "软件研发部"); List<UserInfo> userInfos = Arrays.asList(userInfo1, userInfo2, userInfo3); // toList
List<UserInfo> collect1 = userInfos.stream().collect(Collectors.toList());
System.out.println(collect1); // toSet
Set<UserInfo> collect2 = userInfos.stream().collect(Collectors.toSet());
System.out.println(collect2); // toMap
Map<String, Integer> collect3 = userInfos.stream().collect(Collectors.toMap(UserInfo::getUsername, UserInfo::getAge));
Map<String, UserInfo> collect31 = userInfos.stream().collect(Collectors.toMap(UserInfo::getUsername, (item) -> item));
System.out.println(collect3); // 求和 joining
Double collect4 = userInfos.stream().collect(Collectors.averagingInt(UserInfo::getAge));
String collect5 = userInfos.stream().map(UserInfo::getUsername).collect(Collectors.joining(","));
System.out.println(collect4);
System.out.println(collect5); // 年龄最大的人
Optional<UserInfo> collect6 = userInfos.stream().collect(Collectors.maxBy(Comparator.comparing(UserInfo::getAge)));
System.out.println(collect6); //分组
Map<String, List<UserInfo>> collect7 = userInfos.stream().collect(Collectors.groupingBy(UserInfo::getDept));
System.out.println(JSONUtil.toJsonStr(collect7)); // 多重分组
Map<String, Map<Integer, List<UserInfo>>> collectMap = userInfos.stream().collect(Collectors.groupingBy(UserInfo::getDept,
Collectors.groupingBy(UserInfo::getAge)));
System.out.println(JSONUtil.toJsonStr(collectMap)); //分组统计数量
Map<String, Long> collect8 = userInfos.stream().collect(Collectors.groupingBy(UserInfo::getDept,
Collectors.counting()));
Map<String, Integer> collect9 = userInfos.stream().collect(Collectors.groupingBy(UserInfo::getDept,
Collectors.summingInt(UserInfo::getAge)));
System.out.println(collect8);
System.out.println(collect9); // collectingAndThen
UserInfo collect10 = userInfos.stream().collect(Collectors.collectingAndThen(
Collectors.maxBy(Comparator.comparing(UserInfo::getAge)
), Optional::get)); System.out.println(collect10); }
}

Stream在组合使用才能发货最大的优势,如果仅仅只是单一的操作,其他方法也许更简单高效;

parallel

并行流(Parallel Stream)使用ForkJoinPool实现并行性,利用所有可用CPU内核的优势,并行处理任务。如果任务数超过内核数,则其余任务将等待当前正在运行的任务完成。

可以通过 Runtime.getRuntime().availableProcessors()来获取当前计算机的CPU内核数量。 默认的线程数量就是处理器的数量,也可以通过设置系统属性来改变 System.setProperty("

java.util.concurrent.ForkJoinPool.common.parallelism", "12")

使用场景

Java 使用ForkJoinPool实现并行性,ForkJoinPool派生源流并提交执行;

  • 源数据流应该是可拆分的。例如:ArrayList的数据
  • 在处理问题的时候确实遇到性能问题,否则请不要为了并行而并行。
  • 需要确保线程之间的所有共享资源都是正确同步,否则可能会产生数据不一致问题。

下面的测试方法,cpu是AMD 5600G 情况下 未使用Parallel需要13秒,使用Parallel之后,2秒

public class ParallelStreamTest {

    @Test
public void test1() {
List<Integer> data = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
data.add(i);
} Instant start = Instant.now();
long sum = data.stream()
.map(i -> (int) Math.sqrt(i))
.map(ParallelStreamTest::performComputation)
.reduce(0, Integer::sum); Instant end = Instant.now(); System.out.println(sum);
System.out.printf("Time taken to complete:%s秒", Duration.between(start, end).getSeconds()); } @Test
public void test2() {
List<Integer> data = new ArrayList<>();
for (int i = 0; i < 100000; i++) {
data.add(i);
} Instant start = Instant.now();
long sum = data.stream().parallel()
.map(i -> (int) Math.sqrt(i))
.map(ParallelStreamTest::performComputation)
.reduce(0, Integer::sum); Instant end = Instant.now(); System.out.println(sum);
System.out.printf("Time taken to complete:%s秒", Duration.between(start, end).getSeconds()); } public static int performComputation(int n) {
int sum = 0;
for (int i = 1; i < 100000; i++) {
int a = (n / i);
sum += a;
}
return sum;
}
}

Stream操作debug

对stream操作的代码行打上断点,点击debug中的下图所示图标

此时会弹出一个界面,显示stream的所有操作(数据加载可能有延迟,出现没有数据的情况);上面的卡片选项代表stream流操作,下面对应的内容是操作的结果;一目了然,让我们可以清除的知道整个流执行的过程和结果;

Stream项目中使用场景

  1. 拉取第三方数据时,需要把数据处理完之后存库
  2. 集合数据同步对比(取交集,并集,差集)
  3. 无法使用数据库,或者数据库压力大时,在代码中对数据进行处理

Java8 Stream流使用的更多相关文章

  1. 【转】Java8 Stream 流详解

      当我第一次阅读 Java8 中的 Stream API 时,说实话,我非常困惑,因为它的名字听起来与 Java I0 框架中的 InputStream 和 OutputStream 非常类似.但是 ...

  2. Java8 Stream流

    第三章 Stream流 <Java8 Stream编码实战>的代码全部在https://github.com/yu-linfeng/BlogRepositories/tree/master ...

  3. 这么简单,还不会使用java8 stream流的map()方法吗?

    一.前言 在日常的开发工作中经常碰到要处理list中数据的问题,比如从数据库中查出了很多学生,由于一些原因需要在内存中找出这些学生中的所有姓名,或者把名为"王五"的语文成绩暂时修改 ...

  4. 近万字总结:Java8 Stream流式处理指南

    总结/朱季谦 在实际项目当中,若能熟练使用Java8 的Stream流特性进行开发,就比较容易写出简洁优雅的代码.目前市面上很多开源框架,如Mybatis- Plus.kafka Streams以及F ...

  5. 让代码变得优雅简洁的神器:Java8 Stream流式编程

    原创/朱季谦 本文主要基于实际项目常用的Stream Api流式处理总结. 因笔者主要从事风控反欺诈相关工作,故而此文使用比较熟悉的三要素之一的[手机号]黑名单作代码案例说明. 我在项目当中,很早就开 ...

  6. 关于Java8 Stream流的利与弊 Java初学者,大神勿喷

    题目需求: 1:第一个队伍只要名字为3个字成员的姓名,存储到新集合 2:第一个队伍筛选之后只要前3人:存储到一个新集合 3:第2个队伍只要姓张的成员姓名:存储到一个新集合 4:第2个队伍不要前2人,存 ...

  7. Java8 Stream流API常用操作

    Java版本现在已经发布到JDK13了,目前公司还是用的JDK8,还是有必要了解一些JDK8的新特性的,例如优雅判空的Optional类,操作集合的Stream流,函数式编程等等;这里就按操作例举一些 ...

  8. Java8——Stream流式操作的一点小总结

    我发现,自从我学了Stream流式操作之后,工作中使用到的频率还是挺高的,因为stream配合着lambda表达式或者双冒号(::)使用真的是优雅到了极致!今天就简单分(搬)享(运)一下我对strea ...

  9. 【JDK8】Java8 Stream流API常用操作

    Java版本现在已经发布到JDK13了,目前公司还是用的JDK8,还是有必要了解一些JDK8的新特性的,例如优雅判空的Optional类,操作集合的Stream流,函数式编程等等;这里就按操作例举一些 ...

  10. Java8 Stream流方法

    流是Java API的新成员,它允许以声明性方式处理数据集合(通过查询语句来表达,而不是临时编写一个实现).就现在来说,可以把它们看成遍历数据集的高级迭代器.此外,流还可以透明地并行处理,无需写任何多 ...

随机推荐

  1. 深入理解Vue 3:计算属性与侦听器的艺术

    title: 深入理解Vue 3:计算属性与侦听器的艺术 date: 2024/5/30 下午3:53:47 updated: 2024/5/30 下午3:53:47 categories: 前端开发 ...

  2. 卡方分布和 Zipf 分布模拟及 Seaborn 可视化教程

    卡方分布 简介 卡方分布是一种连续概率分布,常用于统计学中进行假设检验.它描述了在独立抽样中,每个样本的平方偏差之和的分布.卡方分布的形状由其自由度 (df) 参数决定,自由度越大,分布越平缓. 参数 ...

  3. 使用Express写接口

    接口规范 随着前后端分离越来越普遍, 后端接口规范也就越来越重要了,一套良好的接口规范可以提升工作效率, 减少沟通障碍.通常我们都会采用RestfulApi方式来提供接口, 使用 JSON 来传输数据 ...

  4. 用 Sentence Transformers v3 训练和微调嵌入模型

    Sentence Transformers 是一个 Python 库,用于使用和训练各种应用的嵌入模型,例如检索增强生成 (RAG).语义搜索.语义文本相似度.释义挖掘 (paraphrase min ...

  5. Win11系统下的MindSpore环境搭建

    技术背景 笔者尝试过不少编程环境搭建的方案,例如常见的Ubuntu.Deepin.CentOS,也用过很多人力荐的Manjaro,这些发行版在需要办公的条件下,一般都需要结合Windows双系统使用. ...

  6. helm常用命令

    一.helm常用命令 1.查看帮助 helm help 2.创建一个chart包模板 格式: helm create [releasename] 例子: $helm create hello-worl ...

  7. ABC342

    E 建反图 + 拓扑排序. 先求出直接与 \(n\) 连接的点的答,就是最后一辆车的发车时间.然后再做拓扑排序. 假如我们知道点 \(u\) 的答案为 \(ans_u\) 并且 \(u,v\) 相连, ...

  8. C程序函数调用&系统调用

    理解程序的执行 我们要知道CPU可以自由地访问寄存器.内存.另外,程序是由操作系统执行的,所以操作系统能够控制程序的所有执行情况,限制程序的行为. 程序地执行过程: 程序是一个二进制文件,包含程序的代 ...

  9. tampermonkey脚本 百度搜索屏蔽CSDN

    // ==UserScript==// @name         屏蔽CSDN// @namespace    http://tampermonkey.net/// @version      20 ...

  10. 架构师必知的11种API性能优化方法

    前言 接口性能优化是后端开发人员经常碰到的一道面试题,因为它是一个跟开发语言无关的公共问题. 这个问题既可以很简单,也可以相当复杂. 有时候,只需要添加一个索引就能解决. 有时候,代码需要进行重构. ...