天道酬勤,学无止境

关于java8Stream的简单理解

下面直观点了解Stream:

  • Stream是Java8 新增的流特性,目的是让程序员写出高效率、干净、简洁的代码
  • Stream类似于SQL语句,可以对代码进行集合运算和表达
  • Stream就是把元素看成一种流,流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。
     

Stream使用格式成如下图 :

构建Stream  ------------中间操作----------------------终端操作 

注意:在没有终端操作 的前提下,中间操作即生成流不会执行 

数据源:分为串行流Stream和并行流parallelStream()

        列举一下构建串行流的方式,与并行流类似 

  •         构建Stream流的4中途径

 

  1.         通过数组 Arrays.stream
/**
 * Created with IntelliJ IDEA.
 *
 * @Author: DIXian
 * @Date: 2021/07/04/19:31
 */
public class Test04 {
    public static void main(String[] args) {
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
        Stream<Integer> stream = integers.stream();
        stream.forEach(System.out::println);
    }
}

   2.          通过集合Collection.stream

    @Test
    public void test01(){

        ArrayList<Object> list = new ArrayList<>();
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        list.add(6);
        list.stream().forEach(System.out:: println);

    }
}

 3.        通过Stream的静态of()方法Stream.of

    @Test
    public void test02(){
        Stream.of(2, 3, 4, 5).forEach(System.out :: println);
    }
}

4.        通过创建无限流的两种方式

  • Stream.iterate
  • Stream.generate
  @Test
    public void test03(){
        //seed :给定一个初始值 ,后一个参数 T -> t ,此处加个一个截断操作 limit(10)
        Stream.iterate(0, item -> item+2).limit(10).forEach(System.out :: println);
    }

生成流:即对构建好的进行中间操作

  • filter()
  • map()
  • flatMap()
  • distinct()
  • sorted()
  • peek()
  • limit()
  • skip()

1. filter():filter(): 返回结果生成新的流中只包含满足筛选条件的数据。

 public void test04(){
        List<Integer> integers = Arrays.asList(1, 2, 3, 4, 5, 6);
        integers.stream().filter(item -> item.equals(1)).forEach(System.out :: println);


    }

2.map():将流中的元素进行再次加工形成一个新流,流中的每一个元素映射为另外的元素。

// 2、map:返回元素的大写类型和哈希值
        List<String> mzc = Arrays.asList("ma", "zhi", "chu");
        List<String> mzcUpperCase = mzc.stream().
                map(n -> n.toUpperCase()).
                collect(Collectors.toList());
        List<Integer> mzcHashCode = mzc.stream().map(n -> n.hashCode()).collect(Collectors.toList());
        System.out.println("mzcUpperCase:"+mzcUpperCase+" ----- mzcHashCode:"+mzcHashCode);

运行结果:

        mzcUpperCase:[MA, ZHI, CHU] ----- mzcHashCode:[3476, 120571, 98480] 

示例场景:取出商品的所有id,就可以这样写(伪代码):

List<Product> productList = productService.selectAll();

List<Integer> pIds = productList.stream().map(p->p.getId).collect(Collectors.toList());

这样就可以拿到所有商品id的集合。

 3.flatMap():扁平化映射,它具体的操作是将多个stream连接成一个stream,这个操作是针对类似多维数组的,比如集合里面包含集合,相当于降维作用,类似list中的addAll()操作

flatMap是将流中的每个元素都放到一个流中,最后将所有的流合并成一个新流,所有流对象中的元素都合并到这个新生成的流中返回。

// flatMap:将多层集合中的元素取出来,放到一个新的集合中去
        List<Integer> num1 = Arrays.asList(1, 2, 3);
        List<Integer> num2 = Arrays.asList(4, 5, 6);
        List<Integer> num3 = Arrays.asList(7, 8, 9);
        List<List<Integer>> lists = Arrays.asList(num1, num2, num3);
        Stream<Integer> outputStream = lists.stream().flatMap(l -> l.stream());
        List<Integer> flatMapResult = outputStream.sorted().collect(Collectors.toList());
        System.out.println(flatMapResult);

运行结果:[1, 2, 3, 4, 5, 6, 7, 8, 9]

示例场景:取出所有部门人员的姓名,就可以这样写(伪代码):

// 1、取出所有部门

List<Department> departments = ...;

// 2、这个时候可以利用flatMap先将所有部门的所有人员汇聚起来

List<Person> persons = departments.stream.flatMap(d->d.getPersonList()).collect(Collectors.toList());

// 3、再利用map()方法取出

4.distinct():顾名思义,将流中的元素去重之后输出。

List<String> mzc = Stream.of("ma","zhi","chu","zhi","shuo","ma")
        .distinct()
        .collect(Collectors.toList());
System.out.println(mzc);

 运行结果:

[ma, zhi, chu, shuo]

5.sorted():这个很简单了,顾名思义,将流中的元素按照自然排序方式进行排序。

// sorted:自然顺序排序
        List<Integer> nums = Arrays.asList(1, 3, 5, 6, 8, 2);
        List<Integer> sortedNum = nums.stream().sorted().collect(Collectors.toList());
        System.out.println(sortedNum);
 
        // sorted:降序排序
        List<Integer> sortedNum2 = nums.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
        System.out.println(sortedNum2);
 
        // sorted:使用Comparator
        List<Integer> sortedNums3 = nums.stream().sorted(Comparator.comparing(n -> n)).collect(Collectors.toList());
        System.out.println(sortedNums3);
 
        // 不用stream直接顺序排序
        nums.sort(Comparator.comparing(Integer::intValue));
        System.out.println(nums);
 
        //不用stream直接降序排序
        nums.sort(Comparator.comparing(Integer::intValue).reversed());
        System.out.println(nums);

运行结果:

[1, 2, 3, 5, 6, 8]

[8, 6, 5, 3, 2, 1]

[1, 2, 3, 5, 6, 8]

[1, 2, 3, 5, 6, 8]

[8, 6, 5, 3, 2, 1]

6.peek():对流中每个元素执行操作,并返回一个新的流,返回的流还是包含原来流中的元素。

// peek():
        String[] arr = new String[]{"a","b","c","d"};
        Arrays.stream(arr)
                .peek(System.out::println) //a,b,c,d
                .count();
 
        // peek()+filter()
        Stream.of("ma", "zhi", "chu")
                .filter(e -> e.length() > 2)
                .peek(e -> System.out.println(e))
                .collect(Collectors.toList());

运行结果:

a

b

c

d

zhi

chu

7、limit():顾名思义,返回指定数量的元素的流。返回的是Stream里前面的n个元素。

// limit():取出100中的前十个
        List<Integer> limitNum = IntStream.range(1,100).limit(10)
                .boxed()
                .collect(Collectors.toList());
        System.out.println(limitNum);
 
        // limit():取出前4个单词
        List<String> words = Arrays.asList("ma", "zhi", "chu", "wait", "you", "follow");
        List<String> limitWord = words.stream().limit(4).collect(Collectors.toList());
        System.out.println(limitWord);

 

 

运行结果:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

[ma, zhi, chu, wait]

8.skip():和limit()相反,将前几个元素跳过(取出)再返回一个流,如果流中的元素小于或者等于n,就会返回一个空的流。

// skip():跳过前面三个单词再返回
    List<String> words = Arrays.asList("ma", "zhi", "chu", "wait", "you", "follow");
    List<String> skipWord = words.stream().limit(4).collect(Collectors.toList());
    System.out.println(skipWord);
 
    // skip():跳过全部单词再返回
    List<String> emptyWord = words.stream().skip(6).collect(Collectors.toList());
    System.out.println(emptyWord);
 
    // skip():跳过超过单词长度的数目再返回
    List<String> emptyWord2 = words.stream().skip(10).collect(Collectors.toList());
    System.out.println(emptyWord);

运行结果:

[91, 92, 93, 94, 95, 96, 97, 98, 99]

[ma, zhi, chu, wait]

[]

[]

 

聚合操作:即对流进行终端操作

  • forEach()
  • forEachOrdered()
  • toArray()
  • reduce()
  • collect()
  • min()
  • max()
  • count()
  • anyMatch()
  • allMatch()
  • noneMatch()
  • findFirst()
  • findAny()

1.forEach():遍历流中的每一个元素,按照指定的方法执行,执行顺序不一定按照流的顺序。

// foreach:遍历流中每一个元素,执行顺序按照流的顺序
Stream.of(1,2,3,4,5,6).forEach(System.out::println);
// foreach:遍历流中每一个元素,执行顺序不一定按照流的顺序,.parallel()表示创建一个并行流
Stream.of(1,2,3,4,5,6).parallel().forEach(System.out::println);

2.forEachOrdered():遍历流中的每一个元素,按照指定的方法执行,执行顺序按照流的顺序。

// forEachOrdered():遍历流中每一个元素,执行顺序按照流的顺序
Stream.of(1,2,3,4,5,6).forEachOrdered(System.out::println);
// forEachOrdered:遍历流中每一个元素,执行顺序按照流的顺序,.parallel()表示创建一个并行流
Stream.of(1,2,3,4,5,6).parallel().forEachOrdered(System.out::println);

运行结果:

1

2

3

4

5

6

1

2

3

4

5

6

3.toArray():将流中的元素放入到一个数组中

// toArray():将流中的元素放入到一个数组中
String[] strings = Stream.of("ma", "zhi", "chu").toArray(String[]::new);
System.out.println(Arrays.toString(strings));

运行结果:[ma, zhi, chu]

4.reduce():这个方法的主要作用是把 Stream 元素组合起来。它提供一个起始值(种子),然后依照运算规则(BinaryOperator),和前面 Stream 的第一个、第二个、第 n 个元素组合。从这个意义上说,字符串拼接、数值的 sum、min、max、average 都是特殊的 reduce。

// reduce():字符串拼接
String reduceStr1 = Stream.of("ma", "zhi", "chu").reduce("", String::concat);
String reduceStr2 = Stream.of("ma", "zhi", "chu").reduce("", (x,y)->x+y);
System.out.println(reduceStr1);
System.out.println(reduceStr2);
// reduce():求和,identity(起始值)为0
Integer total1 = Stream.of(1,2,3,4).reduce(0, Integer::sum);
Integer total2 = Stream.of(1,2,3,4).reduce(0, (x, y) -> x +y);
System.out.println(total1);
System.out.println(total2);
// 求和,sumValue = 10, 无起始值
Integer total3 = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
System.out.println(total3);
// reduce():求最小值
double minValue = Stream.of(-1.1, 8.8, -2.2, -6.6).reduce(Double.MAX_VALUE, Double::min);
System.out.println(minValue);

运行结果:

mazhichu

mazhichu

10

10

10

-6.6

5.collect():是Stream的一个函数,负责收集流。前面我们说中间操作是将一个流转换成另一个流,这些操作是不消耗流的,但是终端操作会消耗流,产生一个最终结果,collect()就是一个规约操作,将流中的结果汇总。结果是由传入collect()中的Collector定义的

// collect():负责收集流,将结果汇总,比如将下面的流中的结果汇总到一个集合中去
List<Integer> skipNum = IntStream.range(1,100).skip(90)
.boxed()
.collect(Collectors.toList());
System.out.println(skipNum);

运行结果:[91, 92, 93, 94, 95, 96, 97, 98, 99]

6.min():返回流中的最小值

// min():返回流中的最小值
List<Integer> nums = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer minNum = nums.stream().min(Integer::compareTo).get();
Integer min = nums.stream().min((x,y) -> x.compareTo(y)).get();
System.out.println(minNum);
System.out.println(min);

运行结果:

1

1

7.max():返回流中的最大值

// max():返回流中的最大值
List<Integer> num = Arrays.asList(1, 2, 3, 4, 5, 6);
Integer maxNum = num.stream().max(Integer::compareTo).get();
Integer max = num.stream().max(Comparator.comparing(Function.identity())).get();
System.out.println(maxNum);
System.out.println(max);

运行结果:

6

6

8.count():返回流中元素的数量

// count():返回流中元素的数量
List<Integer> ls = Arrays.asList(1,2,3,4,5);
long count = ls.stream().count();
long count1 = ls.stream().filter(l -> l > 2).count();
System.out.println(count);
System.out.println(count1);

运行结果:

5

3

9.anyMatch():Stream 中只要有一个元素符合传入的断言,就返回 true,否则返回false。

// anyMatch():判断流中数据是否有一个复合断言
List<Integer> ins = Arrays.asList(1,2,3,4,5);
boolean b = ins.stream().anyMatch(l -> l > 2);
boolean b1 = ins.stream().anyMatch(l -> l > 5);
System.out.println(b);
System.out.println(b1);
// anyMatch():判断流中数据是否有一个复合断言,如果流为空,永远返回false
List<Integer> inss = Arrays.asList();
boolean b2 = inss.stream().anyMatch(l -> l > 2);
System.out.println(b2);

运行结果:

true

false

false

10.allMatch():Stream 中所有元素都符合传入的断言时返回 true,否则返回false,流为空时总是返回true。

// allMatch():判断流中元素是否都符合断言条件
List<Integer> ints = Arrays.asList(1,2,3,4,5);
boolean c = ints.stream().allMatch(l -> l > 0);
boolean c1 = ints.stream().allMatch(l -> l > 1);
System.out.println(c);
System.out.println(c1);
// allMatch():判断流中元素是否都符合断言条件,如果流为空,永远返回true
List<Integer> emptyList = new ArrayList<>();
boolean c2 = emptyList.stream().allMatch(e -> e > 1);
System.out.println(c2);

运行结果:

true

false

true

11.noneMatch():Stream 中所有元素都不满足传入的断言时返回 true,否则返回false。

// noneMatch():判断流中元素是否都不符合传入的断言条件
List<Integer> numList = Arrays.asList(1,2,3,4,5);
boolean d = numList.stream().noneMatch(l -> l > 6);
boolean d1 = numList.stream().noneMatch(l -> l > 1);
System.out.println(d);
System.out.println(d1);
// noneMatch():判断流中元素是否都不符合传入的断言条件,流为空时永远返回true
List<Integer> numist = Arrays.asList();
boolean d2 = numist.stream().noneMatch(l -> l > 6);
System.out.println(d2);

运行结果:

true

false

true

12.findFirst():总是返回流中的第一个元素,如果流为空,返回一个空的Optional.

// findFirst():返回流中的第一个元素
List<Integer> integers = Arrays.asList(1, 2, 3);
Optional<Integer> first = integers.stream().findFirst();
System.out.println(first);
System.out.println(first.isPresent()); // 判断是否不等于null,isPresent()相当于!=null的判断
System.out.println(first.get());
//findFirst():返回流中的第一个元素,如果流为空,返回一个空的Optional
List<Integer> lls = Collections.EMPTY_LIST;
Optional<Integer> first1 = lls.stream().findFirst();
System.out.println(first1);
System.out.println(first1.isPresent());

运行结果:

Optional[1]

true

1

Optional.empty

false

13.findAny():返回流中的任意一个元素即可,如果流为空,返回一个空的Optional.

// findAny():返回流中任意一个元素,
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6);
Optional<Integer> any = list.stream().findAny();
// 并行流下每次返回的结果会不同
// Optional<Integer> any = list.stream().parallel().findAny();
System.out.println(any);
System.out.println(any.isPresent());
System.out.println(any.get());
// findAny():返回流中任意一个元素,如果流为空,返回一个空的Optional
List<Integer> list1 = Arrays.asList();
Optional<Integer> any1 = list1.stream().findAny();
System.out.println(any1);
System.out.println(any1.isPresent());

运行结果:

Optional[1]

true

1

Optional.empty

false

以上即为Stram的流程操作理解

可参考:https://www.cnblogs.com/mazhichu/p/11983451.html

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • Java8Stream流的sorted()排序===使用Comparator排序
    强烈推荐大佬博客:https://blog.csdn.net/cdw_sunshine/article/details/90738726 https://blog.csdn.net/u014698430/article/details/52850046 https://blog.csdn.net/wjc_hbu/article/details/92853989 sorted()方法排序,一个是Comparable(自然排序),一个是Comparator接口,像Integer、String等这些基本类型的包装类已经实现了Comparable接口, Comparable接口======》java.lang包下的 方法CompareTo(Object o),除了基本类型包装类外,一些常用的pojo类要使用CompareTo(Object o)排序,就要实现Comparable接口,在pojo类里重写compareTo()方法 Comparator接口======》java.util包下的 方法compare(Object a,Object b) 关于Comparable和Comparator接口请看这位大神的博客 Sorted排序一共有两种,自然排序和Comparator接口的排序, 使用sorted()排序 简单List<Integer> 排序=====》 package com.it
  • [源码解析] 当 Java Stream 遇见 Flink
    在分析Alink源码的时候,发现Alink使用了 Java Stream,又去Flink源码搜索,发现Flink也有大量使用。一时兴起,想看看 Java Stream 和 Flink 这种流处理框架的异同点。当然这种比较还是注重于理念和设计思路上的。因为就应用领域和复杂程度来说, Java Stream 和 Flink 属于数量级别的差距。[源码解析] 当 Java Stream 遇见 Flink[ToC]0x00 摘要在分析Alink源码的时候,发现Alink使用了 Java Stream,又去Flink源码搜索,发现Flink也有大量使用。一时兴起,想看看 Java Stream 和 Flink 这种流处理框架的异同点。当然这种比较还是注重于理念和设计思路上的。因为就应用领域和复杂程度来说, Java Stream 和 Flink 属于数量级别的差距。因为Flink的分析文章我写了一些,所以本文源码的分析重点在Java Stream上。0x01 领域1.1 Flink从几个权威来源可以看看Flink本质:我们直接从官网找出Flink本质:Apache Flink® — Stateful Computations over Data Streams,即 数据流上的有状态计算。从github上看:Apache Flink is an open source stream
  • 从 Java 7 和 8 中的现有列表创建不同的列表?(Creating distinct list from existing list in Java 7 and 8?)
    问题
  • Java8经常对集合进行操作的常用API整理
    Java8经常对集合进行操作的常用API整理 前言 java8Stream API提供了大量的集合操作支持,大致分为filter、map、reduce,在这三个大的阶段又会有许多的细分,具体看个人操作需求。操作的案例对象如下: class Student{ String name; Integer age; char gender; LocalDate birth; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public char getGender() { return gender; } public void setGender(char gender) { this.gender = gender; } public LocalDate getBirth() { return birth; } public void setBirth(LocalDate birth) { this.birth = birth; } @Override
  • Java_1.8_新特性
    体验一下哈新特性,下面慢慢介绍 Java8(又称JDK1.8)是Java 语言开发的一个主要版本。Oracle公司于2014年3月18日发布Java 8 支持Lambda表达式函数式接口新的Stream API新的日期API其他特性 Lambda 表达式 lambda表达式:特殊的匿名内部类,语法更简洁 lambda表达式 允许把函数作为一个方法的参数(函数作为方法参数传递),将代码像数据一样传递。 代码演示 public class DemoLambda { public static void main(String[] args) { //匿名内部类 Runnable runnable = new Runnable() { @Override public void run() { System.out.println("子线程出现了。。。。"); } }; //lambda 表达式 Runnable runnable1 = () -> System.out.println("lambda 线程。。。。"); Thread thread = new Thread(runnable); thread.start(); thread = new Thread(runnable1); thread.start(); } } 基本语法 <函数式接口><变量名>=(参数1,参数2
  • Java 7中的Java流(java streams in Java 7)
    问题 我的问题可能太广泛了,答案可能是简单的“否”,但我不得不问。 Java 7中有(Java 8)流*的等效实现吗? 我熟悉(Java 8)流,但是我的项目要求是使用Java 7。 *不要与inputStream和outputStream混淆。 回答1 在官方API中,没有。 Java 7没有更多的公共更新。如果您是客户,您可能仍然会获得较小的更新,但是对于反向移植Stream API而言,这不是(或者非常非常不可能)。 稍微深入一点,您可以看一下StreamSupport。 我从未测试过它,但显然它的目标是将Stream API移植到Java 6/7,如果您想将其与lambda表达式结合使用,也可以使用Retrolambda。 功能性Java可能很有趣。 它与Stream API的意图并不完全相同,但是如果您的目标是过滤/映射/等。 一个列表/数组,它可能适合您的需求。 例如: final List<Integer> b = list(1, 2, 3).map(add.f(-1)); listShow(intShow).println(b); // [0, 1, 2] 最后,您可以研究Scala的Stream API。 由于Scala也在JVM上运行,因此您可以混合代码。 也许这并不是您要找的东西,但是如果需要的话,值得尝试。 回答2
  • 读书笔记.关于es5对象的实例化和继承的简单理解
    1.es5的类实现是通过function构造函数实现的如 通过new的方式实例化这个构造函数。可以添加一下方法如 function Student(name) { this.name=name; this.read=function() { alert(this.name+‘我在读书’) } } let s=new Student(‘zs’); 我们可以给类绑定静态的方法如 Student.eat=function() { alert(this.name+‘在吃’) } 直接通过类调用 Student.eat(); 还可以通过原型链的方式绑定方法和属性 Student.prototype.sleep=function() { alert(this.name+‘在睡觉’) } let s=new Student(‘lx’); s.sleep(); 注意原型链绑定的所有实例共享的。 类的继承 es5一般要用对原型链和对象冒充实现继承 什么是对象冒充继承呢? 如: function Student(name) { this.name=name; this.read=function() { alert(this.name+‘在读书’) } } Student.prototype.sleep=function() { alert(this.name+‘在睡觉’) } function P
  • 关于node.js和js包管理的简单理解
    Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时npm 是 JavaScript 世界的包管理工具,并且是 Node.js 平台的默认包管理工具。通过 npm 可以安装、共享、分发代码,管理项目依赖关系。 1、安装依赖 npm install 2、编译构建# 开发环境 npm run serve 相当于提供一个端口,独立运行# 生产环境 npm run build 相当于打包,需要依赖nginx才能运行 npm设置淘宝源npm config set registry https://registry.npm.taobao.org 解决npm ERR! Unexpected end of JSON input while parsing near第一步:npm install --registry=https://registry.npm.taobao.org --loglevel=silly第二步:npm cache clean --force 来源:https://blog.51cto.com/u_14814727/2733395
  • 关于链表初始化typedef struct LNode{}LNode,*linklist的理解
    在严的数据结构(c语言版) 中看到一段伪代码: typedef struct Node{ ElemType data;//ElemType是本书约定的统一数据元素类型,简单理解成int吧 struct LNode * next; }LNode,*LinkList; typedef的用法不在此细说,可以简单理解为给数据类型取别名。 对于这个代码,目的是定义线性表的单链表储存结构。 结构体部分比较好理解,一个是数据元素data,一个是指向本结构体的结构体指针next。 关键在于LNode与*LinkList—— 抽象出两个句子: typedef struct Node LNode; typedef struct Node* LinkList; 1、先说LNode,参照typede的用法,可以得知LNode就是struct Node1的别名,即LNode==struct Node; 2、再说LinkList,是一个指向该结构体的的指针的别名。其实这个*应该不是跟着LinkList,而是跟在Node后面的。 可以通过这样一个例子可以这样来理解 typedef struct int ElemType typedef struct int* ElemTypePtr 第一个是 定义整型变量的别名 ElemType 第二个是 定义指向整型变量的指针的别名 ElemTypePtr 用起来的话就如下:
  • LDA主题模型学习相关的书籍介绍
    关于LDA主题模型,一度是NLP领域一个非常火的模型,后来深度学习大放异彩,它的热度才慢慢降了下来。 由于数学基础很差,一直没有理解LDA的整个核心。到目前为止,也只是理解了皮毛。记录一下关于LDA主题模型相关的学习资料。 LDA主题模型属于编码简单,但是数学功底要求较高的一个机器学习模型,在搜索引擎和广告领域有用到。按照《LDA 数学八卦》作者靳志辉老师的说法,是一个比较简单的模型,前提是需要数学功底扎实。如果统计学基础扎实,理解LDA主题模型基本是一马平川。 理解LDA主题模型,其实包含4大块的内容: 微积分基础,概率论与数理统计基础, 随机模拟算法, 文本建模思路。LDA数学八卦讲解的思路就是微积分-分布函数-随机模拟-文本建模这条主线的。个人认为,如果数学基础比较差的话,光靠《LDA数学八卦》是很难理解清楚LDA主题模型的。出于弥补数学短板的目的,也是出于兴趣,我前后看了一些书。如下的书籍我觉得还是不错的。 微积分基础 《普林斯顿微积分读本》 这本书从高中数学的基本函数开始,到微积分的各种技巧。讲解细致,学习曲线平缓。 如果这本书觉得枯燥,可以配合如下的4本科普入门。《数学悖论与三次数学危机》《天才引导的历程:数学中的伟大定理》《微积分的历程:从牛顿到勒贝格》《简单微积分 : 学校未教过的超简易入门技巧》 这几本书下来,不敢说理解微积分了
  • 是否有一个简单的小语言编译器(is there a simple compiler for a small language)
    问题 我正在寻找一个简单的编译器来编译一种简单的语言,我需要它来写一篇关于它的论文并学习编译器的工作原理,我不是在寻找一种复杂的东西,只是一种简单的语言(简单我的意思是一个小代码,因为例如 gcc 太大了)。 任何帮助表示赞赏。 回答1 如果你想看代码,Eijiro Sumii 的 MinCaml 编译器给我留下了深刻的印象。 它只有 2000 行长。 它编译了一种非常有趣的源语言。 它生成真正的机器代码,没有这些 namby-pamby C 或 LLVM 的东西:-) 编译代码的速度可与 gcc 和本机 OCaml 编译器相媲美。 编译器是为教学而设计的。 我有没有提到我印象非常深刻? 回答2 我推荐 TinyScheme 或 jonesforth。 回答3 Jack Crenshaw,博士他写了大量关于实用数值方法的文章,很长一段时间都害怕编译器。 他终于厌倦了害怕,并根据他在自学时学到的知识编写了一个关于编译器构造的多部分教程。 有关更多信息,请参阅“让我们构建编译器”。 请注意,它并不完整; 他还没说完就筋疲力尽,但里面有很多容易消化的信息。 回答4 大约1000行代码。 将 Scheme 编译为 LLVM 汇编程序或 C。我认为这非常适合关于编译器的论文。 如果你想更深入,我推荐《SICP》这本书。 回答5 查看 PL/0 的简单编译器(一个类似 pascal 的小子集
  • 无法理解关于linux中函数调用的简单c代码的输出(can't understand the output of the simple c code about function call in linux)
    问题 当我试图理解函数调用时,我编写了一个简单的代码。 但我无法理解它的输出。 #include <stdio.h> int* foo(int n) { int *p = &n; return p; } int f(int m) { int n = 1; return 999; } int main(int argc, char *argv[]) { int num = 1; int *p = foo(num); int q = f(999); printf("[%d]\n[%d]\n", *p, q); /* printf("[%d]\n", *q); */ } 输出: [999] [999] 为什么*p是 999? 然后我修改了我的代码,如下所示: #include <stdio.h> int* foo(int n) { int *p = &n; return p; } int f() { int n = 1; return 999; } int main(int argc, char *argv[]) { int num = 1; int *p = foo(num); int q = f(); printf("[%d]\n[%d]\n", *p, q); /* printf("[%d]\n", *q); */ } 输出: [1] [999] 为什么*p在这里是 1? 我在
  • 关于对WEB标准以及W3C的理解与认识问题
    web标准简单来说可以分为结构、表现和行为。其中结构主要是有HTML标签组成。或许通俗点说,在页面body里面我们写入的标签都是为了页面的结构。表现即指css样式表,通过css可以是页面的结构标签更具美感。行为是指页面和用户具有一定的交互,同时页面结构或者表现发生变化,主要是有js组成。 web标准一般是将该三部分独立分开,使其更具有模块化。但一般产生行为时,就会有结构或者表现的变化,也使这三者的界限并不那么清晰。 W3C对web标准提出了规范化的要求,也就是在实际编程中的一些代码规范:包含如下几点 1.对于结构要求:(标签规范可以提高搜索引擎对页面的抓取效率,对SEO很有帮助) 1)标签字母要小写 2)标签要闭合 3)标签不允许随意嵌套 2.对于css和js来说 1)尽量使用外链css样式表和js脚本。是结构、表现和行为分为三块,符合规范。同时提高页面渲染速度,提高用户的体验。 2)样式尽量少用行间样式表,使结构与表现分离,标签的id和class等属性命名要做到见文知义,标签越少,加载越快,用户体验提高,代码维护简单,便于改版 3)不需要变动页面内容,便可提供打印版本而不需要复制内容,提高网站易用性。 转载于:https://www.cnblogs.com/suyuhuan/p/6015451.html 来源:https://blog.csdn.net/akeyi9167
  • 关于AQS中的enq方法的理解
    自己太笨了,总感觉有点绕,就整理下吧~ private Node enq(final Node node) { //自旋锁 for (;;) { //tail默认就是null Node t = tail; if (t == null) { // Must initialize //因为tail默认是null,所以首次一定会进来 //compareAndSetHead在下面 //也就是首次一定会把一个新的node设置为head if (compareAndSetHead(new Node())) //tail=head=new Node() tail = head; //到这里就是tail和head都会指向new Node } else { //第二次一定进入else发 //假如此时进入了一个线程A,这个A已经被封装成了node传进来 //当前的node的pre指向t,也就是tail,也就是刚才创建的Node, //因为第一行就定义了Node t = tail,而t=head=node node.prev = t; //这里看下面的compareAndSetTail方法 //把tail节点赋值为新传入的node(Thread A),赋值操作就相当于指向 if (compareAndSetTail(t, node)) { //这里的t指的是原来的tail节点,tail指向一开始的new
  • mysql隔离级别命令_MySQl事务隔离级别(命令及简单理解)
    1.查看当前会话隔离级别java select @@tx_isolation;session 2.查看系统当前隔离级别并发 select @@global.tx_isolation;命令行 3.设置当前会话隔离级别事务 set session transaction isolatin level repeatable read;it 4.设置系统当前隔离级别io set global transaction isolation level repeatable read;table 5.命令行,开始事务时select set autocommit=off 或者 start transaction数据 关于隔离级别的理解 1.read uncommitted 能够看到未提交的数据(脏读),举个例子:别人说的话你都相信了,可是可能他只是说说,并不实际作。 2.read committed 读取提交的数据。可是,可能屡次读取的数据结果不一致(不可重复读,幻读)。用读写的观点就是:读取的行数据,能够写。 3.repeatable read(MySQL默认隔离级别) 能够重复读取,但有幻读。读写观点:读取的数据行不可写,可是能够往表中新增数据。在MySQL中,其余事务新增的数据,看不到,不会产生幻读。采用多版本并发控制(MVCC)机制解决幻读问题。 4.serializable 可读,不可写
  • 关于reactor单线程模型的理解
    我们平常应该会遇到一个redis的面试题 Redis 的线程模型是什么? 简单来说就是内部采用的是reactor单线程模型,它内部用的是一个叫做文件事件处理器的东西,这个文件事件处理器这个东西就是单线程的,所以说redis也是一个单线程的模型 这个可能涉及到一些网络编程的知识: 什么是BIO,NIO,两者的区别是什么 BIO (Blocking I/O): 同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。在活动连接数不是特别高(小于单机1000)的情况下,这种模型是比较不错的,可以让每一个连接专注于自己的 I/O 并且编程模型简单,也不用过多考虑系统的过载、限流等问题。线程池本身就是一个天然的漏斗,可以缓冲一些系统处理不了的连接或请求。但是,当面对十万甚至百万级连接的时候,传统的 BIO 模型是无能为力的。因此,我们需要一种更高效的 I/O 处理模型来应对更高的并发量。 NIO (New I/O): NIO是一种同步非阻塞的I/O模型,在Java 1.4 中引入了NIO框架,对应 java.nio 包,提供了 Channel , Selector,Buffer等抽象。NIO中的N可以理解为Non-blocking,不单纯是New。它支持面向缓冲的,基于通道的I/O操作方法。 NIO提供了与传统BIO模型中的 Socket 和 ServerSocket 相对应的
  • 关于C语言函数的简单理解
    关于C语言函数的简单理解 C语言中的函数 在C语言中,函数是构成程序的基本模块。程序的执行从main()函数的入口开始,到main()函数的出口结束,中间循环、往复、迭代的调用一个有一个函数。每个函数分工明确,各司其职,main函数就像是一个总管。虽然main函数有点特殊,但还是可以从使用者的角度对函数分类,将函数分为标准库函数和自定义函数两类。 标准库函数 C语言提供了一些常用的标准库函数,如printf(),scanf()等。符合ANSI C标准的C语言编译器,都必须提供这些库函数。当然,函数的行为也要符合ANSI C的定义。使用ANSI C的库函数,必须在程序的开头把该函数所在的头文件包含进来。例如,使用在math.h内定义的fabs()函数时,只要在程序开头将头文件<math.h>包含到程序中即可。 自定义函数 如果库函数不能满足程序设计者的编程需要,那么就需要自行编写函数来完成自己所需要的功能,这类函数成为自定义函数。 函数定义的基本格式 和使用变量一样,函数使用前必须先定义。函数定义的基本格式为: 返回值类型 函数名(类型 形式参数1,类型 形式参数2,……) { 声明语句序列 可执行语句序列 } 在主函数(main函数)中调用自定义函数的基本格式为: 1.返回值类型为空(void)时: 函数名(实际参数1,实际参数2,……); 2.返回值类型不为空时:
  • 软件工程-总论
    1.软件工作的总论 1.1.代码进化的几个过程 越重要的代码,要求越高。有些代码,能运行即可。有些反复修改和阅读的代码,则最好满足如下四个要求。注意,较高的级别包含低于其级别的要求。 初级:运行。这个是基本要求。即代码的逻辑符合要求。 中级:易读。代码易于阅读理解,在第一个要求的基础上,完善注释、命名、布局等。 高级:性能。代码运行速度快。对于某些对性能要求高的场景,这个很重要。 超高级:精炼。用尽可能少的代码实现想同的功能,包括编程语言的实现形式、算法的选择。 在最初实现,达到初级即可。在以后的维护和迭代中,逐步识别代码的运行维护次数。对于运行次数多的代码,必须达到高级,以保持系统的性能。对于维护次数多的代码,必须满足中级的要求。对于运行和维护次数多高的代码,必须满足超高级的要求。不要一开始将时间浪费在雕琢代码上。如果不分代码的重要性,都按最高级别要求,将大量的时间浪费在雕琢代码上,那就是追求不必要的完美导致效率低下。 1 跑通。 2 结构(按照具体原则和按照需求调整) 3 细节(按照具体原则和按照需求调整) 4 性能 1.2.设计阶段和调试阶段工作分配问题 为什么我们总是在一些低级错误上忙忙碌碌?如何才能避免呢?个人认为主要是一个时间和精力分配的问题。做好一件事情,精力是应该分配在调试阶段,还是设计阶段。这是一个最优化的问题,应该是分配在那个阶段耗费的时间和精力少
  • 关于注释代码的“硬性规则”是什么?(What are your "hard rules" about commenting your code?)
    问题 我已经看到了其他问题,但我仍然对这个主题的覆盖方式不满意。 我想在代码检查中提取一个提炼的事物列表来检查注释。 我相信人们会说一些只会相互抵消的话。 但是,嘿,也许我们可以为每个阵营建立一个列表。 对于那些根本不发表评论的人,列表将非常短:) 回答1 关于评论,我有一个简单的规则:你的代码应该讲述你在做什么; 你的评论应该说明你为什么这样做。 这样,我确保继承我代码的人能够理解代码背后的意图。 回答2 我用元注释评论公共或受保护的函数,如果我记得的话,通常会点击私有函数。 我评论了为什么存在任何足够复杂的代码块(判断调用)。 为什么是重要的部分。 我会评论我是否编写了我认为不是最佳的代码,但我将其保留下来,因为我找不到更聪明的方法,或者我知道我稍后会重构。 我发表评论是为了提醒自己或其他人缺少代码中不存在的功能或即将到来的需求代码(TODO 等)。 我评论是为了解释与类或代码块相关的复杂业务规则。 众所周知,我写了几段来确保下一个人/女孩知道我为什么写了一百行课。 回答3 如果评论已过期(与代码不匹配),请将其删除或更新。 永远不要留下不准确的评论。 回答4 文档就像性; 好的时候非常非常好,坏的时候总比没有好 回答5 尽可能编写易于理解的可读代码。 每当您必须编写过于复杂而无法一目了然的代码时,请添加注释。 还要添加注释来描述您编写的代码背后的业务目的,以便将来更容易维护
  • 9000字,通俗易懂的讲解下Java注解
    对于Java注解,我之前的印象是很模糊的,总觉得这个东西经常听说,也经常用,但是具体是怎么回事,好像没有仔细学习过,说到注解,立马想到@Controller,仅此而已。 对于Java注解,我咨询过一些身边的人,很多人表示: 知道怎么用,不熟悉 不知道你是不是这样?在我没有系统性的学习一边注解的时候,我也是如此,在我花时间学习过注解之后,我觉得,对于注解,最重要的在于理解,很多人也看过不少关于注解的文章,可是过不了多久就会忘记,关于遗忘,这不是个问题,只能说是正常现象。 但是对于一个知识点,你理解的越透彻也就越不容易忘记,所以今天我准备通俗易懂的和大家聊聊Java注解,争取让大家有自己的理解,尽量记住这个重要的知识点! 什么是注解? 我们学习注解的第一步,首先就是先从最基本的开始,看看注解到底是什么? 注解和反射是Java中非常让人容易忽略的东西,但却很重要,在主流的Spring中更是充满了​注解,注解和注释很像,两者其实本质就差不多,注释是给我们程序员看的,而注解呢其实就是给程序看的(关于反射,下一篇咱就开讲) 上面所说希望你着重注意以下两点: 1、注解和注释很像 2、注释是给我们程序员看的,而注解呢其实就是给程序看的 我们初步理解注解就从上面两点开始,我们先看注释,比如这样: 这就是一个注释,那么注释有什么用呢?简单来说就是对相关的类或者方法加以说明,比如这里的Test类