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. 从零开始写 Docker(十六)---容器网络实现(上):为容器插上”网线”

    本文为从零开始写 Docker 系列第十六篇,利用 linux 下的 Veth.Bridge.iptables 等等相关技术,构建容器网络模型,为容器插上"网线". 完整代码见:h ...

  2. 常见的Linux命令

     在 cmd 命令行 或者终端中             可以运行 node.js 命令 也可以 运行 SQL语句 还可以运行 Linux 命令                          Li ...

  3. Ubuntu安装VMware tools后不起作用

    复制和立即适应客户机不起作用. 解决: 1)不需要卸载 2)命令行执行sudo apt-get install open-vm-tools-desktop 3)可能会提示apt-get update或 ...

  4. C#.NET 使用HttpWebRequest发送JSON

    方法: public static string HttpPostJson(string url, string postStr, int timeOut, string charset) { Htt ...

  5. .NET5 ASP.NET CORE 发布到IIS 文件无法替换

    由于默认是:进程内托管.要在IIS里停止网站,才能替换文件. 建议解决方案是:进程外(out-of-process)托管 记事本修改项目的  .csproj 文件(或在VS上,选中web项目,右键-编 ...

  6. nginx虚拟主机实战

    基于nginx部署网站 虚拟主机指的就是一个独立的站点,具有独立的域名,有完整的www服务,例如网站.FTP.邮件等. Nginx支持多虚拟主机,在一台机器上可以运行完全独立的多个站点. 一.为什么配 ...

  7. MapInfo 12.0 及 mapbasic 12.0 安装过程当中遇到的问题的汇总

    目录 MapInfo 12.0 及 mapbasic 12.0 安装过程当中遇到的问题的汇总 C++ 运行时库 Unable to load the CLR (-2147467263) 1) .NET ...

  8. python安装pywifi

    1.Windows安装: 在Dos窗口中输入以下命令: pip install pywifi 如果找不到pip命令,那么需要将Python安装文件夹下Scripts文件夹的绝对路径加入环境变量中. 2 ...

  9. 《最新出炉》系列入门篇-Python+Playwright自动化测试-52- 字符串操作 - 下篇

    1.简介 在日常的自动化测试工作中进行断言的时候,我们可能经常遇到的场景.从一个字符串中找出一组数字或者其中的某些关键字,而不是将这一串字符串作为结果进行断言.这个时候就需要我们对字符串进行操作,宏哥 ...

  10. React Router 6

    路由的概念,可以想像一下路由器,当来了一个请求时,路由器做了什么事情?它会把请求的IP地址和路由表进行匹配,匹配成功后,进行转发,直到目标主机.可以看到路由有三部分组成,一个是请求,一个是路由表,一个 ...