原文:https://www.cnblogs.com/niumoo/p/11880172.html

在使用 Stream 流操作之前你应该先了解 Lambda 相关知识,如果还不了解,可以参考之前文章:还看不懂同事的代码?Lambda 表达式、函数接口了解一下 。

1. Stream 流介绍

Stream 不同于其他集合框架,它也不是某种数据结构,也不会保存数据,但是它负责相关计算,使用起来更像一个高级的迭代器。在之前的迭代器中,我们只能先遍历然后在执行业务操作,而现在只需要指定执行什么操作, Stream 就会隐式的遍历然后做出想要的操作。另外 Stream 和迭代器一样的只能单向处理,如同奔腾长江之水一去而不复返。

由于 Stream 流提供了惰性计算并行处理的能力,在使用并行计算方式时数据会被自动分解成多段然后并行处理,最后将结果汇总。所以 Stream 操作可以让程序运行变得更加高效。

2. Stream 流概念

Stream 流的使用总是按照一定的步骤进行,可以抽象出下面的使用流程。

数据源(source) -> 数据处理/转换(intermedia) -> 结果处理(terminal )

2.1. 数据源

数据源(source)也就是数据的来源,可以通过多种方式获得 Stream 数据源,下面列举几种常见的获取方式。

  • Collection.stream(); 从集合获取流。
  • Collection.parallelStream(); 从集合获取并行流。
  • Arrays.stream(T array) or Stream.of(); 从数组获取流。
  • BufferedReader.lines(); 从输入流中获取流。
  • IntStream.of() ; 从静态方法中获取流。
  • Stream.generate(); 自己生成流

2.2. 数据处理

数据处理/转换(intermedia)步骤可以有多个操作,这步也被称为intermedia(中间操作)。在这个步骤中不管怎样操作,它返回的都是一个新的流对象,原始数据不会发生任何改变,而且这个步骤是惰性计算处理的,也就是说只调用方法并不会开始处理,只有在真正的开始收集结果时,中间操作才会生效,而且如果遍历没有完成,想要的结果已经获取到了(比如获取第一个值),会停止遍历,然后返回结果。惰性计算可以显著提高运行效率。

数据处理演示。

  1. @Test
  2. public void streamDemo(){
  3. List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
  4. // 1. 筛选出名字长度为4的
  5. // 2. 名字前面拼接 This is
  6. // 3. 遍历输出
  7. nameList.stream()
  8. .filter(name -> name.length() == 4)
  9. .map(name -> "This is "+name)
  10. .forEach(name -> System.out.println(name));
  11. }
  12. // 输出结果
  13. // This is Jack
  14. // This is Poul

数据处理/转换操作自然不止是上面演示的过滤 filter 和 map映射两种,另外还有 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等。

2.3. 收集结果

结果处理(terminal )是流处理的最后一步,执行完这一步之后流会被彻底用尽,流也不能继续操作了。也只有到了这个操作的时候,流的数据处理/转换等中间过程才会开始计算,也就是上面所说的惰性计算结果处理也必定是流操作的最后一步。

常见的结果处理操作有 forEach、 forEachOrdered、 toArray、 reduce、 collect、 min、 max、 count、 anyMatch、 allMatch、 noneMatch、 findFirst、 findAny、 iterator 等。

下面演示了简单的结果处理的例子。

  1. /**
  2. * 转换成为大写然后收集结果,遍历输出
  3. */
  4. @Test
  5. public void toUpperCaseDemo() {
  6. List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
  7. List<String> upperCaseNameList = nameList.stream()
  8. .map(String::toUpperCase)
  9. .collect(Collectors.toList());
  10. upperCaseNameList.forEach(name -> System.out.println(name + ","));
  11. }
  12. // 输出结果
  13. // DARCY,CHRIS,LINDA,SID,KIM,JACK,POUL,PETER,

2.4. short-circuiting

有一种 Stream 操作被称作 short-circuiting ,它是指当 Stream 流无限大但是需要返回的 Stream 流是有限的时候,而又希望它能在有限的时间内计算出结果,那么这个操作就被称为short-circuiting。例如 findFirst 操作。

3. Stream 流使用

Stream 流在使用时候总是借助于 Lambda 表达式进行操作,Stream 流的操作也有很多种方式,下面列举的是常用的 11 种操作。

3.1. Stream 流获取

获取 Stream 的几种方式在上面的 Stream 数据源里已经介绍过了,下面是针对上面介绍的几种获取 Stream 流的使用示例。

  1. @Test
  2. public void createStream() throws FileNotFoundException {
  3. List<String> nameList = Arrays.asList("Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter");
  4. String[] nameArr = {"Darcy", "Chris", "Linda", "Sid", "Kim", "Jack", "Poul", "Peter"};
  5. // 集合获取 Stream 流
  6. Stream<String> nameListStream = nameList.stream();
  7. // 集合获取并行 Stream 流
  8. Stream<String> nameListStream2 = nameList.parallelStream();
  9. // 数组获取 Stream 流
  10. Stream<String> nameArrStream = Stream.of(nameArr);
  11. // 数组获取 Stream 流
  12. Stream<String> nameArrStream1 = Arrays.stream(nameArr);
  13. // 文件流获取 Stream 流
  14. BufferedReader bufferedReader = new BufferedReader(new FileReader("README.md"));
  15. Stream<String> linesStream = bufferedReader.lines();
  16. // 从静态方法获取流操作
  17. IntStream rangeStream = IntStream.range(1, 10);
  18. rangeStream.limit(10).forEach(num -> System.out.print(num+","));
  19. System.out.println();
  20. IntStream intStream = IntStream.of(1, 2, 3, 3, 4);
  21. intStream.forEach(num -> System.out.print(num+","));
  22. }

3.2. forEach

forEach 是 Strean 流中的一个重要方法,用于遍历 Stream 流,它支持传入一个标准的 Lambda 表达式。但是它的遍历不能通过 return/break 进行终止。同时它也是一个 terminal 操作,执行之后 Stream 流中的数据会被消费掉。

如输出对象。

  1. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  2. numberList.stream().forEach(number -> System.out.println(number+","));
  3. // 输出结果
  4. // 1,2,3,4,5,6,7,8,9,

3.3. map / flatMap

使用 map 把对象一对一映射成另一种对象或者形式。

  1. /**
  2. * 把数字值乘以2
  3. */
  4. @Test
  5. public void mapTest() {
  6. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  7. // 映射成 2倍数字
  8. List<Integer> collect = numberList.stream()
  9. .map(number -> number * 2)
  10. .collect(Collectors.toList());
  11. collect.forEach(number -> System.out.print(number + ","));
  12. System.out.println();
  13. numberList.stream()
  14. .map(number -> "数字 " + number + ",")
  15. .forEach(number -> System.out.println(number));
  16. }
  17. // 输出结果
  18. // 2,4,6,8,10,12,14,16,18,
  19. // 数字 1,数字 2,数字 3,数字 4,数字 5,数字 6,数字 7,数字 8,数字 9,

上面的 map 可以把数据进行一对一的映射,而有些时候关系可能不止 1对 1那么简单,可能会有1对多。这时可以使用 flatMap。下面演示使用 flatMap把对象扁平化展开。

  1. /**
  2. * flatmap把对象扁平化
  3. */
  4. @Test
  5. public void flatMapTest() {
  6. Stream<List<Integer>> inputStream = Stream.of(
  7. Arrays.asList(1),
  8. Arrays.asList(2, 3),
  9. Arrays.asList(4, 5, 6)
  10. );
  11. List<Integer> collect = inputStream
  12. .flatMap((childList) -> childList.stream())
  13. .collect(Collectors.toList());
  14. collect.forEach(number -> System.out.print(number + ","));
  15. }
  16. // 输出结果
  17. // 1,2,3,4,5,6,

3.4. filter

使用 filter 进行数据筛选,挑选出想要的元素,下面的例子演示怎么挑选出偶数数字。

  1. /**
  2. * filter 数据筛选
  3. * 筛选出偶数数字
  4. */
  5. @Test
  6. public void filterTest() {
  7. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  8. List<Integer> collect = numberList.stream()
  9. .filter(number -> number % 2 == 0)
  10. .collect(Collectors.toList());
  11. collect.forEach(number -> System.out.print(number + ","));
  12. }

得到如下结果。

  1. 2,4,6,8,

3.5. findFirst

findFirst 可以查找出 Stream 流中的第一个元素,它返回的是一个 Optional 类型,如果还不知道 Optional 类的用处,可以参考之前文章 Jdk14都要出了,还不能使用 Optional优雅的处理空指针? 。

  1. /**
  2. * 查找第一个数据
  3. * 返回的是一个 Optional 对象
  4. */
  5. @Test
  6. public void findFirstTest(){
  7. List<Integer> numberList = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
  8. Optional<Integer> firstNumber = numberList.stream()
  9. .findFirst();
  10. System.out.println(firstNumber.orElse(-1));
  11. }
  12. // 输出结果
  13. // 1

findFirst 方法在查找到需要的数据之后就会返回不再遍历数据了,也因此 findFirst 方法可以对有无限数据的 Stream 流进行操作,也可以说 findFirst 是一个 short-circuiting 操作。

3.6. collect / toArray

Stream 流可以轻松的转换为其他结构,下面是几种常见的示例。

  1. /**
  2. * Stream 转换为其他数据结构
  3. */
  4. @Test
  5. public void collectTest() {
  6. List<Integer> numberList = Arrays.asList(1, 1, 2, 2, 3, 3, 4, 4, 5);
  7. // to array
  8. Integer[] toArray = numberList.stream()
  9. .toArray(Integer[]::new);
  10. // to List
  11. List<Integer> integerList = numberList.stream()
  12. .collect(Collectors.toList());
  13. // to set
  14. Set<Integer> integerSet = numberList.stream()
  15. .collect(Collectors.toSet());
  16. System.out.println(integerSet);
  17. // to string
  18. String toString = numberList.stream()
  19. .map(number -> String.valueOf(number))
  20. .collect(Collectors.joining()).toString();
  21. System.out.println(toString);
  22. // to string split by ,
  23. String toStringbJoin = numberList.stream()
  24. .map(number -> String.valueOf(number))
  25. .collect(Collectors.joining(",")).toString();
  26. System.out.println(toStringbJoin);
  27. }
  28. // 输出结果
  29. // [1, 2, 3, 4, 5]
  30. // 112233445
  31. // 1,1,2,2,3,3,4,4,5

3.7. limit / skip

获取或者扔掉前 n 个元素

  1. /**
  2. * 获取 / 扔掉前 n 个元素
  3. */
  4. @Test
  5. public void limitOrSkipTest() {
  6. // 生成自己的随机数流
  7. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  8. ageList.stream()
  9. .limit(3)
  10. .forEach(age -> System.out.print(age+","));
  11. System.out.println();
  12. ageList.stream()
  13. .skip(3)
  14. .forEach(age -> System.out.print(age+","));
  15. }
  16. // 输出结果
  17. // 11,22,13,
  18. // 14,25,26,

3.8. Statistics

数学统计功能,求一组数组的最大值、最小值、个数、数据和、平均数等。

  1. /**
  2. * 数学计算测试
  3. */
  4. @Test
  5. public void mathTest() {
  6. List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
  7. IntSummaryStatistics stats = list.stream().mapToInt(x -> x).summaryStatistics();
  8. System.out.println("最小值:" + stats.getMin());
  9. System.out.println("最大值:" + stats.getMax());
  10. System.out.println("个数:" + stats.getCount());
  11. System.out.println("和:" + stats.getSum());
  12. System.out.println("平均数:" + stats.getAverage());
  13. }
  14. // 输出结果
  15. // 最小值:1
  16. // 最大值:6
  17. // 个数:6
  18. // 和:21
  19. // 平均数:3.5

3.9. groupingBy

分组聚合功能,和数据库的 Group by 的功能一致。

  1. /**
  2. * groupingBy
  3. * 按年龄分组
  4. */
  5. @Test
  6. public void groupByTest() {
  7. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  8. Map<String, List<Integer>> ageGrouyByMap = ageList.stream()
  9. .collect(Collectors.groupingBy(age -> String.valueOf(age / 10)));
  10. ageGrouyByMap.forEach((k, v) -> {
  11. System.out.println("年龄" + k + "0多岁的有:" + v);
  12. });
  13. }
  14. // 输出结果
  15. // 年龄10多岁的有:[11, 13, 14]
  16. // 年龄20多岁的有:[22, 25, 26]

3.10. partitioningBy

  1. /**
  2. * partitioningBy
  3. * 按某个条件分组
  4. * 给一组年龄,分出成年人和未成年人
  5. */
  6. public void partitioningByTest() {
  7. List<Integer> ageList = Arrays.asList(11, 22, 13, 14, 25, 26);
  8. Map<Boolean, List<Integer>> ageMap = ageList.stream()
  9. .collect(Collectors.partitioningBy(age -> age > 18));
  10. System.out.println("未成年人:" + ageMap.get(false));
  11. System.out.println("成年人:" + ageMap.get(true));
  12. }
  13. // 输出结果
  14. // 未成年人:[11, 13, 14]
  15. // 成年人:[22, 25, 26]

3.11. 进阶 - 自己生成 Stream 流

  1. /**
  2. * 生成自己的 Stream 流
  3. */
  4. @Test
  5. public void generateTest(){
  6. // 生成自己的随机数流
  7. Random random = new Random();
  8. Stream<Integer> generateRandom = Stream.generate(random::nextInt);
  9. generateRandom.limit(5).forEach(System.out::println);
  10. // 生成自己的 UUID 流
  11. Stream<UUID> generate = Stream.generate(UUID::randomUUID);
  12. generate.limit(5).forEach(System.out::println);
  13. }
  14. // 输出结果
  15. // 793776932
  16. // -2051545609
  17. // -917435897
  18. // 298077102
  19. // -1626306315
  20. // 31277974-841a-4ad0-a809-80ae105228bd
  21. // f14918aa-2f94-4774-afcf-fba08250674c
  22. // d86ccefe-1cd2-4eb4-bb0c-74858f2a7864
  23. // 4905724b-1df5-48f4-9948-fa9c64c7e1c9
  24. // 3af2a07f-0855-455f-a339-6e890e533ab3

上面的例子中 Stream 流是无限的,但是获取到的结果是有限的,使用了 Limit 限制获取的数量,所以这个操作也是 short-circuiting 操作。

4. Stream 流优点

4.1. 简洁优雅

正确使用并且正确格式化的 Stream 流操作代码不仅简洁优雅,更让人赏心悦目。下面对比下在使用 Stream 流和不使用 Stream 流时相同操作的编码风格。

  1. /**
  2. * 使用流操作和不使用流操作的编码风格对比
  3. */
  4. @Test
  5. public void diffTest() {
  6. // 不使用流操作
  7. List<String> names = Arrays.asList("Jack", "Jill", "Nate", "Kara", "Kim", "Jullie", "Paul", "Peter");
  8. // 筛选出长度为4的名字
  9. List<String> subList = new ArrayList<>();
  10. for (String name : names) {
  11. if (name.length() == 4) {
  12. subList.add(name);
  13. }
  14. }
  15. // 把值用逗号分隔
  16. StringBuilder sbNames = new StringBuilder();
  17. for (int i = 0; i < subList.size() - 1; i++) {
  18. sbNames.append(subList.get(i));
  19. sbNames.append(", ");
  20. }
  21. // 去掉最后一个逗号
  22. if (subList.size() > 1) {
  23. sbNames.append(subList.get(subList.size() - 1));
  24. }
  25. System.out.println(sbNames);
  26. }
  27. // 输出结果
  28. // Jack, Jill, Nate, Kara, Paul

如果是使用 Stream 流操作。

  1. // 使用 Stream 流操作
  2. String nameString = names.stream()
  3. .filter(num -> num.length() == 4)
  4. .collect(Collectors.joining(", "));
  5. System.out.println(nameString);

4.2. 惰性计算

上面有提到,数据处理/转换(intermedia) 操作 map (mapToInt, flatMap 等)、 filter、 distinct、 sorted、 peek、 limit、 skip、 parallel、 sequential、 unordered 等这些操作,在调用方法时并不会立即调用,而是在真正使用的时候才会生效,这样可以让操作延迟到真正需要使用的时刻。

下面会举个例子演示这一点。

  1. /**
  2. * 找出偶数
  3. */
  4. @Test
  5. public void lazyTest() {
  6. // 生成自己的随机数流
  7. List<Integer> numberLIst = Arrays.asList(1, 2, 3, 4, 5, 6);
  8. // 找出偶数
  9. Stream<Integer> integerStream = numberLIst.stream()
  10. .filter(number -> {
  11. int temp = number % 2;
  12. if (temp == 0 ){
  13. System.out.println(number);
  14. }
  15. return temp == 0;
  16. });
  17. System.out.println("分割线");
  18. List<Integer> collect = integerStream.collect(Collectors.toList());
  19. }

如果没有 惰性计算,那么很明显会先输出偶数,然后输出 分割线。而实际的效果是。

  1. 分割线
  2. 2
  3. 4
  4. 6

可见 惰性计算 把计算延迟到了真正需要的时候。

4.3. 并行计算

获取 Stream 流时可以使用 parallelStream 方法代替 stream 方法以获取并行处理流,并行处理可以充分的发挥多核优势,而且不增加编码的复杂性。

下面的代码演示了生成一千万个随机数后,把每个随机数乘以2然后求和时,串行计算和并行计算的耗时差异。

  1. /**
  2. * 并行计算
  3. */
  4. @Test
  5. public void main() {
  6. // 生成自己的随机数流,取一千万个随机数
  7. Random random = new Random();
  8. Stream<Integer> generateRandom = Stream.generate(random::nextInt);
  9. List<Integer> numberList = generateRandom.limit(10000000).collect(Collectors.toList());
  10. // 串行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
  11. long start = System.currentTimeMillis();
  12. int sum = numberList.stream()
  13. .map(number -> number * 2)
  14. .mapToInt(x -> x)
  15. .sum();
  16. long end = System.currentTimeMillis();
  17. System.out.println("串行耗时:"+(end - start)+"ms,和是:"+sum);
  18. // 并行 - 把一千万个随机数,每个随机数 * 2 ,然后求和
  19. start = System.currentTimeMillis();
  20. sum = numberList.parallelStream()
  21. .map(number -> number * 2)
  22. .mapToInt(x -> x)
  23. .sum();
  24. end = System.currentTimeMillis();
  25. System.out.println("并行耗时:"+(end - start)+"ms,和是:"+sum);
  26. }

得到如下输出。

  1. 串行耗时:1005ms,和是:481385106
  2. 并行耗时:47ms,和是:481385106

效果显而易见,代码简洁优雅。

5. Stream 流建议

5.1 保证正确排版

从上面的使用案例中,可以发现使用 Stream 流操作的代码非常简洁,而且可读性更高。但是如果不正确的排版,那么看起来将会很糟糕,比如下面的同样功能的代码例子,多几层操作呢,是不是有些让人头大?

  1. // 不排版
  2. String string = names.stream().filter(num -> num.length() == 4).map(name -> name.toUpperCase()).collect(Collectors.joining(","));
  3. // 排版
  4. String string = names.stream()
  5. .filter(num -> num.length() == 4)
  6. .map(name -> name.toUpperCase())
  7. .collect(Collectors.joining(","));

5.1 保证函数纯度

如果想要你的 Stream 流对于每次的相同操作的结果都是相同的话,那么你必须保证 Lambda 表达式的纯度,也就是下面亮点。

  • Lambda 中不会更改任何元素。
  • Lambda 中不依赖于任何可能更改的元素。

这两点对于保证函数的幂等非常重要,不然你程序执行结果可能会变得难以预测,就像下面的例子。

  1. @Test
  2. public void simpleTest(){
  3. List<Integer> numbers = Arrays.asList(1, 2, 3);
  4. int[] factor = new int[] { 2 };
  5. Stream<Integer> stream = numbers.stream()
  6. .map(e -> e * factor[0]);
  7. factor[0] = 0;
  8. stream.forEach(System.out::println);
  9. }
  10. // 输出结果
  11. // 0
  12. // 0
  13. // 0

文中代码都已经上传到

https://github.com/niumoo/jdk-feature/blob/master/src/main/java/net/codingme/feature/jdk8/Jdk8Stream.java

超强的Lambda Stream流操作的更多相关文章

  1. 还看不懂同事的代码?超强的 Stream 流操作姿势还不学习一下

    Java 8 新特性系列文章索引. Jdk14都要出了,还不能使用 Optional优雅的处理空指针? Jdk14 都要出了,Jdk8 的时间处理姿势还不了解一下? 还看不懂同事的代码?Lambda ...

  2. 全面吃透JAVA Stream流操作,让代码更加的优雅

    全面吃透JAVA Stream流操作,让代码更加的优雅 在JAVA中,涉及到对数组.Collection等集合类中的元素进行操作的时候,通常会通过循环的方式进行逐个处理,或者使用Stream的方式进行 ...

  3. lamda表达式与Stream 流操作,reduce,flatMap,groupingBy等

    /** * 符合lambda表达式的接口也叫函数式接口: * 除了默认方法和Object类的方法外,只有一个抽象方法的接口才能符合lambda表达式的要求 * 可以使用@FunctionalInter ...

  4. Node学习笔记(一):stream流操作

    NodeJs中谈及较多的可能就是Stream模块了,先写一个简单的ajax回调 $.post("index.php",{data:'aaa',order:'ccc'},functi ...

  5. Stream 流操作

     Stream 类 先看下面的图 Stream 是所有流的抽象基类(不能被实例化,需要使用他的派生类FileStream/MemoryStream/BufferedStream).流是字节序列的抽象概 ...

  6. stream流操作List工具类

    工作中操作List对于程序猿来说是"基本操作",为了更加便利,对JDK8的新特性stream流进行二次封装.话不多说,直接上代码 package com.mydemo; impor ...

  7. 深度掌握 Java Stream 流操作,让你的代码高出一个逼格!

    概念 Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选.排序.聚合等. Stream 的操作符大体上分为两种:中间操作符和终止操作符 中 ...

  8. .net System.IO.Stream 流操作类(FileStream等)

    Stream 是所有流的抽象基类.流是字节序列的抽象概念. 流涉及到的3个基本操作: 读取流,读取是指从流到数据结构(如字节数组)的数据传输. 写入流,写入是指从数据结构到流的数据传输. 流查找,查找 ...

  9. java8 stream流操作的flatMap(流的扁平化)

    https://mp.weixin.qq.com/s/7Fqb6tAucrl8UmyiY78AXg https://blog.csdn.net/Mark_Chao/article/details/80 ...

随机推荐

  1. Codeforces Round #549 (Div. 2) D 数学

    https://codeforces.com/contest/1143/problem/D 题意 有nk个城市,第1,k+1,2k+1,...,(n-1)k+1城市有餐厅,你每次能走l距离,a为起始位 ...

  2. GreenPlum 大数据平台--segment 失效问题恢复

    1,问题检查 [gpadmin@greenplum01 conf]$ psql -c "select * from gp_segment_configuration where status ...

  3. 利用Jenkins打包并远程部署SpringBoot应用

    本文Jenkins版本2.190.2,为19年11月最新 1.安装Jenkins.Git.Maven和JDK Jenkins安装参考:https://www.cnblogs.com/zhi-leaf/ ...

  4. 【2019年07月08日】A股最便宜的股票

    查看更多A股最便宜的股票:androidinvest.com/CNValueTop/ 便宜指数 = PE + PB + 股息 + ROE,四因子等权,数值越大代表越低估. 本策略只是根据最新的数据来选 ...

  5. SpringBoot第十七篇:定时任务

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11076555.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   相信大家对 ...

  6. Debug 路漫漫-11:Python: TypeError: 'generator' object is not subscriptable

    调试程序,出现以下错误: Python: TypeError: 'generator' object is not subscriptable “在Python中,这种一边循环一边计算的机制,称为生成 ...

  7. Kelp.Net是一个用c#编写的深度学习库

    Kelp.Net是一个用c#编写的深度学习库 基于C#的机器学习--c# .NET中直观的深度学习   在本章中,将会学到: l  如何使用Kelp.Net来执行自己的测试 l  如何编写测试 l  ...

  8. python多条插入问题

    多条插入用excutemany(listtuple) #coding=utf-8 import MySQLdb import traceback sqlstr= "insert into t ...

  9. Vue.js 源码分析(十) 基础篇 ref属性详解

    ref 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素:如果用在子组件上,引用就指向组件实例,例如: ...

  10. eclipse中修改项目名

    把项目名springboot-demo改成springboot-rabbitmq 第一步: 选中项目,点击F2,修改项目名第二步: 修改.project文件第三步: 修改.setting/org.ec ...