Java基础:Stream编程在日常开发中的使用

日常的开发工作中,我们都不可避免的需要进行类型的转换,比如list转为map,一个实体类转为另外一个实体类,或者说进行一些过滤的动作,或者说需要获取一个排好序的list。 对于这些需求,我们都可以写出对应的解决方案,但是如何写得优雅,同时控制代码的行数,这时候就需要Stream来帮忙了。

Stream的特点

  1. Stream不存储数据,这是Stream与Collection最大的区别之一
  2. Stream不是数据结构,而是从Collection、数组、IO等获取输入
  3. 不会改变原来的数据结构
  4. 可以是无数元素集合
  5. 支持lazy操作
  6. 每一个intermediate操作都将会以lazy的方式执行,并且返回一个新的Stream,比如filter()方法【可以使用多次】
  7. Terminal操作将会结束Stream,并且返回最终结果,比如collect()方法【只能使用一次】
  8. Stream无法被重用,即对Stream的每一次操作都会产生一个全新的Stream
  9. 支持函数式编程

创建Stream

创建Stream的几种常用的方法

Numeric 创建测试数据

LongStream、IntStream可以通过 range() 和 rangeclose()方法创建在范围内的一个整数数组

public static void main(String[] args) {
    //创建区间在 1<= x <=10
    LongStream.rangeClosed(1,10);
    //创建区间在 1<= x <10
    LongStream.range(1,12);
}

容器创建Stream【最常用】

1.8之后,Collection开始有对应的Stream方法,对应的Collection实体可以直接使用 如 list.Stream()的调用方法

File 创建Stream

java.io 和 java.nio.File包支持通过Stream对I/O进行操作。比如,你可以读取一个文本文件,并且创建String类型的Stream,该Stream元素序列中的每一个元素就代表了该文件的每一行文本

public static void main(String[] args) throws IOException {
    Stream<String> fileStream = Files.lines(Paths.get("test.txt"), Charset.forName("UTF-8"));
}

预备知识,几个函数式接口的简介

Predicate<? super T> predicate 这个函数式接口一般用于true or false的判断
**Function<? super T, ? extends R> mapper ** 这个函数式接口需要返回对应的数据类型,入参为一个
**Consumer<? super T> action **对元素进行消耗操作,返参为void
BinaryOperator accumulator 函数式接口,lamda函数是有两个入参的,返参是对应的实体类类型

接下来主要介绍Internidate操作和Terminal操作,这两种操作在日常开发中非常的常用

Stream的Intermediate操作

在Stream中 filter、sort、map之类的操作被称为Intermediate操作。这类操作的结果都是一个全新的Stream类型。多个Intermediate操作构成了一个流水线。除此之外,Intermediate操作的执行都是通过lazy的方式,直到遇到最后的Terminal操作。
一个Stream一旦执行了Intermediate操作或者Terminal操作,将无法再次操作。

Stream的Intermediate方法列表

方法描述
distinct去重,返回一个不含重复元素的Stream
filter过滤,执行之后,会返回一个满足predicate条件判断的stream
limit类似mysql中的limit操作,但是这个操作只能取前面几个数,如果取的长度超过Stream中的元素个数也不影响
map对每个元素执行操作,并返回一个新的对象,一般用于对象转换
skip执行这个操作之后,会跳过前几个元素直接到后面的元素
peek对元素执行consume操作,但是会返回和之前一样元素的全新Stream,看起来会像是对元素进行debug
sorted排序操作,会返回一个经过排序的Stream,被排序类需要时Comparable的子类,或者使用Compartor.compare()指定对应的排序规则。
flatmapflatmap为一对多操作,会将类型为Stream<Stream>的Stream转化为Stream的Stream,并且产生一个新的Stream

distinct

使用方法及备注

distinct是通过实体类的equals方法进行比较从而得出是否重复,所以自定义的实体类需要重写equals方法和hashcode方法,但是一般不建议修改这两个方法。如果需要使用这个方法的话,建议是Java中已有的实体类而不是自定义的实体类。
实在需要对当前的Stream去重的话,建议使用filter方法+额外的Set的方式进行去重

public static void main(String[] args) {
    Stream.of(1,2,3,4,5,5,5,5).distinct().forEach(System.out::println);
}

输出样例

1
2
3
4
5

使用场景

该方法使用的场景比较狭隘,一般使用与Java已有实体类之间的比较,以及用于一些算法的测试与校验。

filter

使用方法以及备注

对流中的元素按一定规则进行过滤,符合对应规则的则会返回为ture,则会通过这个过滤器,否则不会通过

public static void main(String[] args) {
    //只保留偶数
    Stream.of(1,2,3,4,5,6,7,8,8,8)
        .filter(i -> i%2==0)
        .forEach(System.out::println);
}

输出样例

2
4
6
8
8
8

参数解释

Predicate<? super T> predicate 这个函数式接口一般用于true or false的判断
这个使用lamda的时候,return的为Boolean值即可

使用场景

获取商品中Level 1的商品
public static void main(String[] args) {
    Stream.of(
        new ProductionDO("prod1",20.0,1,1),
        new ProductionDO("prod2",20.0,1,2),
        new ProductionDO("prod3",40.0,2,3),
        new ProductionDO("prod4",50.0,3,4)
    ).filter(
        prod-> prod.getLevel().equals(1)
    ).forEach(System.out::println);
}

输出样例

Production{name=‘prod1’, price=20.0, level=1, id=1}
Production{name=‘prod2’, price=20.0, level=1, id=2}

去重的操作

由于数据库原因,获取到的数据具有交叉重复的,所以需要进行去重操作

//场景2:将商品列表去重
public static void main(String[] args) {
    HashSet<Object> distinctSet = new HashSet<>();
    Stream.of(
        new ProductionDO("prod1",20.0,1,1),
        new ProductionDO("prod1",20.0,1,1),
        new ProductionDO("prod2",20.0,1,2),
        new ProductionDO("prod2",20.0,1,2),
        new ProductionDODO("prod3",40.0,2,3),
        new ProductionDO("prod4",50.0,3,4)
    ).filter(
        //使用set的add方法返回的是boolean值,所以可以使用这特性来进行去重
        prod->distinctSet.add(prod.getId())
    ).forEach(System.out::println);

}

输出样例

Production{name=‘prod1’, price=20.0, level=1, id=1}
Production{name=‘prod2’, price=20.0, level=1, id=2}
Production{name=‘prod3’, price=40.0, level=2, id=3}
Production{name=‘prod4’, price=50.0, level=3, id=4}

limit

使用方法及备注

使用limit取前面的限制元素个数,入参则为获取的元素个数, 使用这个方法之后,会取前面相对应的个数的元素,如果limit值大于Stream的size,则会直接获取全部元素

public static void main(String[] args) {
    Stream.of(1,2,3,3,4,5,6,6,7)
        .limit(2)
        .forEach(System.out::println);
}

输出样例

1
2

使用场景

一般会结合Filter进行操作,从而达到过滤后获取对应个数元素的操作
例如:获取3个Level为1的商品

public static void main(String[] args) {
    Stream.of(
        new ProductionDO("prod1",20.0,1,1),
        new ProductionDO("prod5",20.0,1,5),
        new ProductionDO("prod2",20.0,1,2),
        new ProductionDO("prod6",20.0,1,6),
        new ProductionDO("prod3",40.0,2,3),
        new ProductionDO("prod4",50.0,3,4)
    ).filter(
        prod->prod.getLevel().equals(1)
    ).limit(3).forEach(System.out::println);
}

skip

skip操作与limit相反,skip是跳过前几个取后面的

//skip操作
public static void main(String[] args) {
    //skip操作
    Stream.of(1,2,3,4,5,6,7,8,9,10)
        .skip(4)
        .forEach(System.out::println);

    System.out.println("============");
    //skip 配合limit进行分页操作
    Stream.of(1,2,3,4,5,6,7,8,9,10)
        .skip(3)
        .limit(2)
        .forEach(System.out::println);
}

输出样例

5
6
7
8
9
10
============
4
5

使用场景

跟limit的使用场景差不多,跟其他Itemediate操作进行结合

map

使用方法以及备注

map方法一般用于实体类的转换,就像数据库中获取出的DO类转为需要输出的DTO
这里的示例为S1的Stream转S2的Stream并输出

//map操作,将S1实体类转为S2
public static void main(String[] args) {
    Stream.of(new S1("name value"))
        .map(s -> new S2(s.getName()))
        .forEach(System.out::println);
}

static class S1{
    String name;

    public S1(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "S1{" +
            "name='" + name + '\'' +
            '}';
    }
}

static class S2{
    String value;

    public S2(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "S2{" +
            "value='" + value + '\'' +
            '}'; 
    }
}

输出样例

S2{value=‘name value’}

参数解释

Function<? super T, ? extends R> mapper 这个函数式接口需要返回对应的数据类型
可以在里面进行一顿操作之后转换为对应的数据类型

使用场景

各种实体类的之间的转化,业务方面比较常见的就是DO转为DTO,将数据库的实体类经过整合之后转给前端,或者从前端中获得DTO类再转给数据库。
场景:ProductionDO转ProductionDTO

public static void main(String[] args) {
    Stream.of(
        new ProductionDO("prod1",20.0,1,1,"Jovan"),
        new ProductionDO("prod2",20.0,1,1,"Jovan"),
        new ProductionDO("prod3",30.0,1,1,"Jovan"),
        new ProductionDO("prod4",40.0,3,1,"Jovan"),
        new ProductionDO("prod5",50.0,2,1,"Jovan")
    ).map(prod->{
        ProductionResp productionResp = new ProductionResp(prod.getName(), prod.getPrice(), prod.getLevel(), prod.getId());
        productionResp.setRequestDate(new Date());
        return productionResp;
    }).forEach(System.out::println);
}

输出示例

ProductionResp{name=‘prod1’, price=20.0, level=1, id=1, requestDate=Fri Mar 17 22:53:49 CST 2023}
ProductionResp{name=‘prod2’, price=20.0, level=1, id=1, requestDate=Fri Mar 17 22:53:49 CST 2023}
ProductionResp{name=‘prod3’, price=30.0, level=1, id=1, requestDate=Fri Mar 17 22:53:49 CST 2023}
ProductionResp{name=‘prod4’, price=40.0, level=3, id=1, requestDate=Fri Mar 17 22:53:49 CST 2023}
ProductionResp{name=‘prod5’, price=50.0, level=2, id=1, requestDate=Fri Mar 17 22:53:49 CST 2023}

peek

使用方法以及备注

对Stream中所有元素进行Consume操作【Consume操作可以是存入另外一个容器,可以是写进日志,可以是打印至控制台输出】,并返回一个与原Stream类型、元素相同的全新Stream。该操作不会对Stream产生任何改变,看起来更像是debug的操作。

public static void main(String[] args) {
    int result = IntStream.rangeClosed(1,10)
        .peek(System.out::println)
        .sum();
    System.out.println(result);

}

输出样例

1
2
3
4
5
6
7
8
9
10
55

参数解释

Consumer<? super T> action 添加对应的函数式接口对当前的元素进行操作,函数的入参为Stream中的元素,没有返参,或者说返参为void

使用场景

一般用于平时写代码的debug,上线的时候有必要则检查是否有peek的操作,不过peek操作也有利于log的编写,看项目的安全性要求。

场景:记录每次商品filter后的记录

//将每次的筛选后的结果log出来
public static void main(String[] args) {
    long count = Stream.of(
        new ProductionDO("prod1", 20.0, 1, 1, "Jovan"),
        new ProductionDO("prod2", 20.0, 1, 2, "Jovan"),
        new ProductionDO("prod3", 30.0, 5, 3, "Jovan"),
        new ProductionDO("prod4", 45.0, 3, 4, "Jovan"),
        new ProductionDO("prod5", 50.0, 2, 5, "Jovan")
    ).filter(
        prod -> prod.getLevel() >= 2
    ).peek(prod -> {
        System.out.println("=====取leve>=2的商品====");
        System.out.println(prod);
    }).filter(
        prod -> prod.getPrice() > 40.0
    ).peek(prod -> {
        System.out.println("=====取价格大于40的商品====");
        System.out.println(prod);
    }).count();

    System.out.println("经过筛选后,剩余的商品数为"+count);
}

输出样例,由于Peek操作是lazy的,所以输出的时候有的价格大于40的会输出到前面

===== 取level>=2 的商品====
Production{name=‘prod3’, price=30.0, level=5, id=3}
===== 取level>=2的商品 ====
Production{name=‘prod4’, price=45.0, level=3, id=4}
===== 取价格大于40的商品 ====
Production{name=‘prod4’, price=45.0, level=3, id=4}
===== 取level>=2的商品 ====
Production{name=‘prod5’, price=50.0, level=2, id=5}
===== 取价格大于40的商品 ====
Production{name=‘prod5’, price=50.0, level=2, id=5}
经过筛选后,剩余的商品数为2

sorted

使用方法以及备注

Stream进行sorted操作时,会将元素进行正序排序,如果没有传入参数的sorted()方法中,Stream中的实体必须是Comparable的子类,否则会报错。或者选择另外一个方法sorted(Comparator<? super T> comparator)
进行排序操作,使用这个方法可以对实体类进行某个属性的正序,逆序操作,同时也可以先后比较多个元素进行操作,如示例中的,按照rank进行排序,但是rank相同时可以使用name进行第二排序,同理其他的情况。
个人比较推荐使用带参数的sorted(),这样的话就无需修改对应的实体类就能根据不同的情况进行不同的排序了。

public static void main(String[] args) {
    //对Comparable的子类进行排序
    Stream.of(44,6,41,43,5)
        .sorted()
        .forEach(System.out::println);

    System.out.println("===================");

    //使用Comparator进行正序排序
    Stream.of(new S3(1,"kim"),
              new S3(4,"jovan"),
              new S3(2, "ken"),
              new S3(3,"tina"))
        .sorted(Comparator.comparing(S3::getRank))
        .forEach(System.out::println);
    System.out.println("===================");

    //使用Comparator进行逆序排序
    Stream.of(new S3(1,"kim"),
              new S3(4,"jovan"),
              new S3(2, "ken"),
              new S3(3,"tina"))
        .sorted(Comparator.comparing(S3::getRank).reversed())
        .forEach(System.out::println);
    System.out.println("===================");

    //使用Comparator选择多个元素先后进行排序
    //这个样例中,如果rank相同则会比较name
    Stream.of(new S3(1,"kim"),
              new S3(4,"jovan"),
              new S3(2, "ken"),
              new S3(3,"tina"),
              new S3(3,"rays"))
        .sorted(Comparator.comparing(S3::getRank).thenComparing(S3::getName))
        .forEach(System.out::println);
    System.out.println("===================");

}


static class S3{
    int rank;
    String name;

    public S3(int rank, String name) {
        this.rank = rank;
        this.name = name;
    }

    public int getRank() {
        return rank;
    }

    public void setRank(int rank) {
        this.rank = rank;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "S3{" +
            "rank=" + rank +
            ", name='" + name + '\'' +
            '}';
    }
}

输出样例

5
6
41
43
44
===================
S3{rank=1, name=‘kim’}
S3{rank=2, name=‘ken’}
S3{rank=3, name=‘tina’}
S3{rank=4, name=‘jovan’}
===================
S3{rank=4, name=‘jovan’}
S3{rank=3, name=‘tina’}
S3{rank=2, name=‘ken’}
S3{rank=1, name=‘kim’}
===================
S3{rank=1, name=‘kim’}
S3{rank=2, name=‘ken’}
S3{rank=3, name=‘rays’}
S3{rank=3, name=‘tina’}
S3{rank=4, name=‘jovan’}
===================

使用场景

大多数业务下,数据在排好序之后就能获取一段连续的特征值,进而进行不同的操作。

flatmap

使用方法以及备注

对Stram进行一对多的操作,并且将产生的所有数据放在一个Stream里。
也就是原本会得到Stream<Stream>结果的,经过flatmap之后就会变成Stream
也就是说把其中多个流扁平化成一个流

    //flatmap操作
    public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 5)
                .flatMap(i-> Stream.of(i, i*2, i*i))
                .forEach(System.out::println);
        System.out.println("========");

        //使用String数组进行单词统计的例子
        //一步步的返回,看看flatmap进行了什么操作
        String[] strings = {"Hello", "world"};
        Stream<String> stream = Arrays.stream(strings);
        Stream<String[]> stream1 = stream.map(word -> word.split(""));
        //[['H','e','l','l','o'],['w','o','r','l','d']]
        //->['H','e','l','l','o','w','o','r','l','d']
        //flatmap一般是用于处理流的,将多个流合并成一个流,如果没有涉及流的话,可以直接使用map就好
        Stream<String> stringStream = stream1.flatMap(Arrays::stream);
        stringStream.distinct().forEach(System.out::println);

    }

输出示例

1
2
1
2
4
4
3
6
9
4
8
16
5
10
25
========
H
e
l
o
w
r
d

使用场景

一般是分类之后的扁平流操作

Stream的Terminal操作

Stream的Terminal操作会终结Stream的流水线(pipeline的继续进行,最终返回一个非Stream类型的结果。因此,在流水线中执行了Terminal方法之后,Strem将会被关闭。)

Stream的Terminal操作会终结Stream的流水线(pipeline的继续进行,最终返回一个非Stream类型的结果。因此,在流水线中执行了Terminal方法之后,Strem将会被关闭。)

方法描述
matchmatch的返回类型是布尔类型,主要是用于判断是否存在匹配条件(Predicate)的元素,match操作具体如下
allMatch(): 若所有的元素都匹配条件,则结果为true,否则为false
anyMatch(): 只要有一个元素匹配条件,则结果为true,否则为false
noneMatch(): 若所有元素都不匹配则结果为true,否则为false
findfind类型的操作会返回Stream中的某个元素Optional, 一般情况下,我们会在一个包含filter操作的流水线中使用find操作返回过滤后的某个值,find类型的具体操作如下
Optional<T> findFirst() : 返回Stream中的第一个元素
Optional<T> findAny():返回Stream中的任意一个元素
foreachforEach操作用于对Stream中的每一个元素执行consume函数,Steram提供了两种方式的foreach,具体如下
forEach(Consumer<T> consumer): 为每个元素执行consume函数,但是在并发流中对source stream 或者upstream的执行并不会按顺序来forEachOrdered(Consumer<T> consumer): 为每个元素执行consume函数,保证在并发流中对source stream 或者upstream的执行顺序
countcount操作用于返回Stream中的元素个数,返回值为一个long类型的数值
maxmax类型的操作会根据Comparator接口的定义,返回Stream中最大的那个元素
minmax类型的操作会根据Comparator接口的定义,返回Stream中最小的那个元素
collectcollect操作可以将Stream中的元素合到一个新的集合中,比如map、list、set
reducereduce操作通过BinaryOperator 函数式接口对Stream中的所有元素逐次进行计算,得到一个最终值并且返回

match

使用方法以及备注

match的参数是一个Predicate 函数式接口,所以只需要输入对应的判断条件即可得出对应的结果,函数的意思就是allMatch-> 全匹配 ,anyMatch-> 任意匹配, noneMatch-> 一个都不匹配

//match 操作
public static void main(String[] args) {
    // 判断Stream中的元素是否全部匹配
    System.out.println(Stream.of(1,2,3,4,5,6).allMatch(i-> i>0));

    // 判断Stream中的元素是否有一个元素进行匹配
    System.out.println(Stream.of(1,2,3,4,5,6).anyMatch(i->i>3));

    // 判断Stream中的元素是否没有一个元素匹配
    System.out.println(Stream.of(1,2,3,4,5,6).noneMatch(i->i>10));
}

输出样例

true
true
true

参数解释

Predicate<? super T> predicate 返参为Boolean值由于判断当前的元素是否符合某种条件

使用场景

一般用于判断一组数据是否符合某个预期条件,然后再进行某些操作,跟filter有异曲同工之妙,但是Filter是Itermediate操作。有时也可以用于Debug,看看某个list的元素是否符合预期的数值。

find

使用方法以及备注

find操作一般是在filter之后进行操作,查找在过滤之后是否能获取到对应的元素,也是多数用于debug的情况,或者进行某些log的编写, filter+findAny跟match的操作是一致的,如果需要这样操作,倒不如直接用match

//find 操作
public static void main(String[] args) {
    //过滤后获取第一个
    System.out.println(Stream.of(1,2,3,4,5,6).filter(i->i>3).findFirst().get());

    //过滤后获取任意一个
    System.out.println(Stream.of(1,2,3,4,5,6).filter(i->i<10).findAny().isPresent());

    //过滤后获取任意一个
    System.out.println(Stream.of(1,2,3,4,5,6).filter(i->i>10).findAny().isPresent());
}

输出样例

4
true
false

forEach

使用方法以及备注

一般会在进行一系列操作之后对元素进行输出或者是记录日志,或者是使用元素执行其他步骤,总之就是对元素进行一个最终的操作。

//forEach 操作
public static void main(String[] args) {
    Stream.of(1,2,3,4,5,6).forEach(System.out::println);
    System.out.println("======");
    Stream.of(1,2,3,4,5,6).forEachOrdered(System.out::println);
}

输出样例

1
2
3
4
5
6
======
1
2
3
4
5
6

参数解释

Consumer<? super T> action 对元素进行消耗输出,返参为void

count

使用方法以及备注

计算出当前的Stream中有多少个元素, 一般用于Stream进行filter操作后,或者进行其他itermediate操作后进行一个计数,如果没有这些操作,直接使用list.size()更快。

public static void main(String[] args) {
    System.out.println(Stream.of(1,2,3,4,5,6).count());
}

输出样例

6

max

使用方法以及备注

返回当前Stream里最大的元素,需要传入Comparator参数以进行比较并获得最大的那个参数,输入的Comparator可以是比较实体类里面的某个参数,和sort()方法中的使用一致

public static void main(String[] args) {
    Optional<Integer> max = Stream.of(1, 2, 3, 4, 5, 6).max(Comparator.comparingInt(o -> o));
    System.out.println(max.get());
}

输出样例

6

使用场景

可以用于评级的操作,获取评分最高分的那个分数或者是实体类,或者是排行榜中的最大数,如果是数据库连表连得比较多,可以使用Stream进行筛选数据

min

使用方法以及备注
返回当前Stream里最小的元素,需要传入Comparator参数以进行比较并获得最大的那个参数

public static void main(String[] args) {
    Optional<Integer> min = Stream.of(1, 2, 3, 4, 5, 6).min(Comparator.comparingInt(o -> o));
    System.out.println(min.get());
}

输出样例

6

参数解释和使用场景参照max方法

reduce

使用方法以及备注

reduce的功能是对元素进行操作,**BinaryOperator **中 使用前一个函数的结果与下一个元素一起进行操作,返回操作后的结果,所以reduce操作后最终只会有一个结果产生

reduce有两个方法
T reduce(T identity, BinaryOperator accumulator);
Optional reduce(BinaryOperator accumulator);
第一个是带初始化seed的方法,这个方法会把seed 当做累加器的第一个参数,然后进行计算,最后返回的也是对应类型的参数
第二个则是只有累加器的参数,累加器会把stream中的第一个参数作为累加器的第一个参数。

所以在选择这两个方法的时候,就是考虑当时的使用场景是否有初始化的值需要使用。

//reduce的使用
public static void main(String[] args) {
    //IntStream中的元素和
    Integer reduce = Stream.of(1, 2, 3, 4, 5, 6).reduce(0, Integer::sum);
    System.out.println(reduce);
    //返回长度最长的
    Optional<String> string = Stream.of("test", "hello", "tmd").reduce((s1, s2) -> s1.length() > s2.length() ? s1 : s2);
    System.out.println(string.get());
}

输出样例

21
hello

参数解释

BinaryOperator accumulator 函数式接口,lamda函数是有两个入参的,返参是对应的实体类类型

使用场景

1)可以用来获取元素的总和,最小值、最大值、字符串最长的、最短的
2)根据需要,对元素进行拼接,重组

collect

collector方法需要传入一个Collector参数进行转化,可以转化为list、map、set的集合,详情请看后续的Collector篇

附录

参考书籍

《Java高并发编程详解 – 深入理解并发核心库》 汪文君 著 2020

文章中的实体类源码

package com.concurrent.stream;


/**
* @author Jovan
* @date 2022/12/15 16:13
*/
public class ProductionDO {
    /**
     * 商品名称
     */
    private String name;
    /**
     * 价格
     */
    private Double price;
    /**
     * 商品等级
     */
    private Integer level;
    /**
     * 模拟数据库id
     */
    private Integer id;
    /**
     * 创建者,一般用户界面不会展示这个字段
     */
    private String createUser;

    public ProductionDO(String name, Double price) {
        this.name = name;
        this.price = price;
    }

    public ProductionDO(String name, Double price, Integer level, Integer id) {
        this.name = name;
        this.price = price;
        this.level = level;
        this.id = id;
    }

    public ProductionDO(String name, Double price, Integer level, Integer id, String createUser) {
        this.name = name;
        this.price = price;
        this.level = level;
        this.id = id;
        this.createUser = createUser;
    }

    public ProductionDO() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public Integer getLevel() {
        return level;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Override
    public String toString() {
        return "Production{" +
            "name='" + name + '\'' +
            ", price=" + price +
            ", level=" + level +
            ", id=" + id +
            '}';
    }
}
package com.concurrent.stream;

import java.util.Date;

/**
* @author Jovan
* @date 2023/3/17 22:41
*/
public class ProductionResp {
    /**
     * 商品名称
     */
    private String name;

    /**
     * 价格
     */
    private Double price;

    /**
     * 商品等级
     */
    private Integer level;

    /**
     * 模拟数据库id
     */
    private Integer id;

    /**
     * 请求时间
     */
    private Date requestDate;

    public ProductionResp(String name, Double price, Integer level, Integer id) {
        this.name = name;
        this.price = price;
        this.level = level;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public Double getPrice() {
        return price;
    }

    public Integer getLevel() {
        return level;
    }

    public Integer getId() {
        return id;
    }

    public Date getRequestDate() {
        return requestDate;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public void setLevel(Integer level) {
        this.level = level;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setRequestDate(Date requestDate) {
        this.requestDate = requestDate;
    }

    @Override
    public String toString() {
        return "ProductionResp{" +
            "name='" + name + '\'' +
            ", price=" + price +
            ", level=" + level +
            ", id=" + id +
            ", requestDate=" + requestDate +
            '}';
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值