stream笔记

1、 创建流stream

1.2、 stream中间操作

1.3、 add and andAll difference:

1.4、 终止操作

1.5、 场景

1、创建流stream:

流(Stream) 到底是什么 呢 ?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“ 集合讲的是数据 , 流讲的是 计 算 ! ”
注意 :
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

1.1 Stream 的操作三个步骤
  • 创建 建 Stream
    一个数据源(如:集合、数组),获取一个流
  • 中间操作
    一个中间操作链,对数据源的数据进行处理
  • 终止操作( ( 终端操作) )
    一个终止操作,执行中间操作链,并产生结果

在这里插入图片描述

default Stream stream()List list = new ArrayList<>(); Stream stream = list.stream(); //获取一个顺序流返回一个顺序流
default Stream parallelStream()Stream parallelStream = list.parallelStream(); //获取一个并行流返回一个并行流
Integer[] nums = new Integer[10]; Stream stream1 = Arrays.stream(nums);通过 Arrays 中的 stream() 获取一个数组流
//迭代 Stream stream3 = Stream.iterate(0, (x) -> x + 2).limit(10); stream3.forEach(System.out::println);创建无限流
Stream stream4 = Stream.generate(Math::random).limit(2);生成
        //下面两个遍历一样
        stream4.forEach(System.out::println);
//        stream4.forEach(s->{
//            System.out.println(s);
//        });

1.2:stream中间操作
filter接收 Lambda , 从流中排除某些元素。把符合条件过滤出来
limit截断流,使其元素不超过给定数量。
skip(n)跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补
distinct筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
map接收 Lambda , 将元素转换成其他形式或提取信息。接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流,流中流
sorted()自然排序 comparable
sorted(Comparator com)定制排序

1.2.1 limit、skip、distinct、
public class Employee {
    private int id;
    private String name;
    private int age;
    private Double salary;

}
List<Employee> emps = Arrays.asList(
        new Employee(102, "李四", 59, 6666.66),
        new Employee(101, "张三", 18, 9999.99),
        new Employee(103, "王五", 28, 3333.33),
        new Employee(104, "赵六", 8, 7777.77),
        new Employee(104, "赵六", 8, 7777.77),
        new Employee(104, "赵六", 8, 7777.77),
        new Employee(105, "田七", 38, 5555.55)
);
    //内部迭代:迭代操作 Stream API 内部完成(filter  过滤时需要迭代一下)
    @Test
    public void test2() {
        //所有的中间操作不会做任何的处理
        Stream<Employee> stream = emps.stream()
                //断言,传入参数,返回布尔值
                .filter((e) -> {
                    System.out.println("测试中间操作");
                    return e.getAge() <= 35;
                });

        //只有当做终止操作时,所有的中间操作会一次性的全部执行,称为“惰性求值”
        stream.forEach(System.out::println);
    }

    //外部迭代
    @Test
    public void test3() {
        Iterator<Employee> it = emps.iterator();

        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }


    //找到三个之后就不往下找了。提高效率
    @Test
    public void test4() {
        emps.stream()
                .filter((e) -> {
                    System.out.println("短路!"); // &&  ||
                    return e.getSalary() >= 5000;
                }).limit(3)
                .forEach(System.out::println);
    }


    //大于5000的前两个跳过,也就是不取
    @Test
    public void test5() {
        emps.parallelStream()
                .filter((e) -> e.getSalary() >= 5000)
                .skip(2)
                .forEach(System.out::println);
    }


    //去重。如果是对象,hashcode和equals  进行比较,所以要去重。  lambda 已经重写的了去重操作
    @Test
    public void test6() {
        emps.stream()
                .distinct()
                .forEach(System.out::println);
    }

1.2.2 map and flatMap:
  •    @Test
          public void test7() {
              Stream<String> str = emps.stream()
                      .map((e) -> e.getName());
      
              System.out.println("-------------------------------------------");
      
              List<String> strList = Arrays.asList("aaa", "bbb", "ccc", "ddd", "eee");
      
              Stream<String> stream = strList.stream()
                      .map(String::toUpperCase);
      
              stream.forEach(System.out::println);
      
              //TODO:下面两种一样的。内部迭代
      //        emps.stream().map(Employee::getName).forEach(System.out::println);
              emps.stream().map(s -> s.getName()).forEach(System.out::println);
      
      
              Stream<Stream<Character>> stream2 = strList.stream()
                      .map(TestStreamApI::filterCharacter);
      
              stream2.forEach((sm) -> {
                  sm.forEach(System.out::println);
              });
      
              System.out.println("---------------------------------------------");
      
              Stream<Character> stream3 = strList.stream()
                      .flatMap(TestStreamApI::filterCharacter);
      
              stream3.forEach(System.out::println);
      
      
              System.out.println("----------------------------------------------");
              //解决结果两次for循环
              strList.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);
      
          }
      
          public static Stream<Character> filterCharacter(String str) {
              List<Character> list = new ArrayList<>();
      
              for (Character ch : str.toCharArray()) {
                  list.add(ch);
              }
      
              return list.stream();
          }
    

1.2.3 sort 自然排序和定制排序:
public class Employee {
    private int id;
    private String name;
    private int age;
    private Double salary;

}
  @Test
    public void test9() {
        emps.stream()
                .map(Employee::getName)
                .sorted()
                .forEach(System.out::println);

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

        emps.stream()
                .sorted((x, y) -> {
                    if (x.getAge() == y.getAge()) {
                        //两种排序方式
                        return x.getName().compareTo(y.getName());
                    } else {
                        return Integer.compare(x.getAge(), y.getAge());
                    }
                }).forEach(System.out::println);

        System.out.println("---------------------------------------------");
        List<String> list1 = Arrays.asList("zzzzzz", "aaaa", "bbbb", "cccc", "ddddd", "eeeee");
        list1.stream().sorted().forEach(System.out::println);

    }

1.3add and andAll difference:

add 和addall 区别:

  • 1、add 是把集合添加进去,addAll 是把集合中的元素添加进去
 @Test
    public void test8() {
        List<String> list1 = Arrays.asList("aaaa", "bbbb", "cccc", "ddddd", "eeeee");
        List list2 = new ArrayList<>();
        list2.add(1111);
        list2.add(2222);

        list2.add(list1);

        System.out.println(list2);
//[1111, 2222, [aaaa, bbbb, cccc, ddddd, eeeee]]
        System.out.println("================================");

        list2.addAll(list1);
        System.out.println(list2);
        //addall  是把集合中的元素取出来添加list1中。  里面的[aaaa, bbbb, cccc, ddddd, eeeee]   是上面添加的
        //[1111, 2222, [aaaa, bbbb, cccc, ddddd, eeeee], aaaa, bbbb, cccc, ddddd, eeeee]

    }

1.4 终止操作
anyMatch检查是否至少匹配一个元素
noneMatch检查是否没有匹配的元素
findFirst返回第一个元素
findAny返回当前流中的任意元素
count返回流中元素的总个数
max返回流中最大值Optional max = emps.stream().map(Employee::getSalary).max((v1,v2)->Double.compare(v1,v2)); .max(Double::compare); .max((v1,v2)->v1.compareTo(v2));
min返回流中最小值
reduce归约。可以将流中元素反复结合起来,得到一个值。
collect将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法.collect(Collectors.toList());.collect(Collectors.toSet()); 放到hashset中:: .collect(Collectors.toCollection(HashSet::new)); .collect(Collectors.maxBy(Double::compare)); 总薪水: .collect(Collectors.summingDouble(Employee::getSalary)); 平均值: .collect(Collectors.averagingDouble(Employee::getSalary)); 总数: .collect(Collectors.counting()); 计算薪水总函数(最大值,最小值、平均值、数量):.collect(Collectors.summarizingDouble(Employee::getSalary)); 分组:.collect(Collectors.groupingBy(Employee1::getStatus)); 多级分组:Collectors.groupingBy(Employee1::getStatus, Collectors.groupingBy()。 分区:.collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000)); joining: .collect(Collectors.joining(“,”, “----”, “----”));
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee1 {
    private int id;
    private String name;
    private int age;
    private double salary;
    private Status status;

    public enum Status {
        FREE, BUSY, VOCATION;
    }
}
List<Employee1> empss = Arrays.asList(
        new Employee1(102, "李四", 59, 6666.66, Employee1.Status.BUSY),
        new Employee1(101, "张三", 18, 9999.99, Employee1.Status.FREE),
        new Employee1(103, "王五", 28, 3333.33, Employee1.Status.VOCATION),
        new Employee1(104, "赵六", 8, 7777.77, Employee1.Status.BUSY),
        new Employee1(104, "赵六", 8, 7777.77, Employee1.Status.FREE),
        new Employee1(104, "赵六", 8, 7777.77, Employee1.Status.FREE),
        new Employee1(105, "田七", 38, 5555.55, Employee1.Status.BUSY)
);

1.4.1allmatch、anyMatch、noneMatch、max、min
 @Test
    public void test10() {
        //返回false ,并不是匹配所有元素
        boolean bl = empss.stream()
                .allMatch((e) -> e.getStatus().equals(Employee1.Status.BUSY));
        System.out.println(bl);

//true,有这个样的元素
        boolean bl1 = empss.stream()
                .anyMatch((e) -> e.getStatus().equals(Employee1.Status.BUSY));

        System.out.println(bl1);

        //没有匹配元素 false
        boolean bl2 = empss.stream()
                .noneMatch((e) -> e.getStatus().equals(Employee1.Status.BUSY));
        System.out.println(bl2);

//得到最大值
        Optional<Double> max = emps.stream().map(Employee::getSalary)
//                .max(Double::compare);
        .max((v1,v2)->Double.compare(v1,v2));
      // .max((v1,v2)->v1.compareTo(v2));  
      System.out.println(max.get());

//得到最小值,的成员
        Optional<Employee> min = emps.stream().min((v1, v2) -> Double.compare(v1.getSalary(), v2.getSalary()));
        System.out.println(min.get());

//得到薪水的最小值,把薪水通过map映射出来
        Optional<Double> min1 = emps.stream().map(item -> item.getSalary()).min(Double::compare);
        System.out.println(min1.get());
    }

1.4.2reduce
  @Test
    public void test20() {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

        /**
         * x=0,y=1,x=x+y=1
         * x=1 y=2 x=x+y=3
         * ...........
         */
        Integer sum = list.stream()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);


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

        Optional<Double> op = emps.stream()
                .map(Employee::getSalary)
                .reduce(Double::sum);
        System.out.println(op.get());


        System.out.println("====");
        double identity = 0.0;
        double d = emps.stream().map(Employee::getSalary)
                .reduce(identity, (x, y) -> x + y);
        System.out.println(d);

        //为什么这个是optional  因为可能为空,而上面那个为什么不是?因为有初始值,所以即使你传入的为null,但结果也不会为空。
        Optional<Double> reduce = emps1.stream().map(Employee::getSalary).filter(item -> {
                    System.out.println("item" + item);
                    boolean equals = item == null;
                    return !equals;
                })
                .reduce(Double::sum);
        //如果上面为null的话,下面可以使用这个变成null。如果有值还会删除你的结果的。
        System.out.println(reduce.orElse(null));

/**
 * itemnull
 * itemnull
 * null
 */
    }

1.4.3 collect
 @Test
    public void test21() {
//        把名字收集到list中去
        List<String> list = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.toList());

        list.forEach(System.out::println);

        System.out.println("----------------------------------");
//        把名字收集到set中去
        Set<String> set = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.toSet());

        set.forEach(System.out::println);

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

        //把名字放到hashset中去
        HashSet<String> hs = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.toCollection(HashSet::new));
        hs.forEach(System.out::println);
    }


    @Test
    public void test22() {

        Optional<Double> max = emps.stream()
                .map(Employee::getSalary)
                .collect(Collectors.maxBy(Double::compare));
        //如果上面为空的话就为null,不为空就为  计算的值
        /**
         * 结果
         * 9999.99
         * 9999.99
         */
        System.out.println(max.orElse(null));
        System.out.println(max.get());


        Optional<Employee> op = emps.stream()
                .collect(Collectors.minBy((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())));

        System.out.println(op.get());


        //总薪水
        Double sum = emps.stream()
                .collect(Collectors.summingDouble(Employee::getSalary));
        System.out.println(sum);


        //平均值
        Double avg = emps.stream()
                .collect(Collectors.averagingDouble(Employee::getSalary));
        System.out.println(avg);

        //总数
        Long count = emps.stream()
                .collect(Collectors.counting());

        System.out.println(count);

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

        DoubleSummaryStatistics dss = emps.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));

        System.out.println(dss.getMax());
    }


 //计算薪水总和,里面能得到一系列的东西。总函数的形式
    @Test
    public void test28() {

        DoubleSummaryStatistics statistics = emps.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(statistics.getMax());
        System.out.println(statistics.getAverage());
        System.out.println(statistics.getCount()
        );
        System.out.println(statistics.getMin());
        System.out.println(statistics.getSum());


        IntSummaryStatistics summaryStatistics = emps.stream().collect(Collectors.summarizingInt(Employee::getAge));

//        Collectors.summarizingDouble(Employee::getSalary);
//        System.out.println(sum.get());
    }


 //分组
    @Test
    public void test23() {
        Map<Employee1.Status, List<Employee1>> map = empss.stream()
                .collect(Collectors.groupingBy(Employee1::getStatus));
        System.out.println(map);

//        遍历hashmap
        map.forEach((key, value) -> {
            System.out.println("key" + key + "======value" + value);
        });
    }


    //多级分组
    @Test
    public void test27() {
        Map<Employee1.Status, Map<String, List<Employee1>>> map = empss.stream()
                .collect(Collectors.groupingBy(Employee1::getStatus, Collectors.groupingBy((e) -> {
                    if (((Employee1) e).getAge() >= 60) {
                        return "老年";
                    } else if (((Employee1) e).getAge() >= 35) {
                        return "中年";
                    } else {
                        return "成年";
                    }

                })));

        System.out.println(map);
    }


    //分区
    @Test
    public void test24() {
        Map<Boolean, List<Employee>> map = emps.stream()
                .collect(Collectors.partitioningBy((e) -> e.getSalary() >= 5000));
        System.out.println(map);
    }

    //收集名字,分割,添加前后缀
    @Test
    public void test25() {
        String str = emps.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",", "----", "----"));

        System.out.println(str);
    }

    //计算薪水总和。
    @Test
    public void test26() {
        Optional<Double> sum = emps.stream()
                .map(Employee::getSalary)
                .collect(Collectors.reducing(Double::sum));
        //结果保存小数点后保存两位
        System.out.println(sum.get().floatValue());
    }


    //计算薪水总和,里面能得到一系列的东西。总函数的形式
    @Test
    public void test28() {

        DoubleSummaryStatistics statistics = emps.stream()
                .collect(Collectors.summarizingDouble(Employee::getSalary));
        System.out.println(statistics.getMax());
        System.out.println(statistics.getAverage());
        System.out.println(statistics.getCount()
        );
        System.out.println(statistics.getMin());
        System.out.println(statistics.getSum());


        IntSummaryStatistics summaryStatistics = emps.stream().collect(Collectors.summarizingInt(Employee::getAge));

//        Collectors.summarizingDouble(Employee::getSalary);
//        System.out.println(sum.get());
    }


1.4.5 将名字拼接起来
@Test
public void test29(){
    String collect = emps.stream().map(Employee::getName)
            .distinct()
            .sorted()
            .collect(Collectors.joining(" "));
    System.out.println(collect);
}
//第二种方法:

  //使用计算的形式,如果没有起始值,不知道加起来的值是字符串还是什么。如果没有起始值需要强制转换一下
        String reduce1 = emps.stream().map(Employee::getName).distinct().sorted()
                .reduce("", String::concat);
        System.out.println(reduce1);



结果 张三李四王五田七赵六

场景

map

  • 1、list集合,实体中根据某个字段进行去重操作。
//以username+accountid 作为唯一,把重复的去掉,返回还得是list集合的形式。
代码解释:CalculationDetailLogBatch对象 。
1、流操作:logList.stream()将logList(一个List<CalculationDetailLogBatch>类型的列表)
        转换为一个流(Stream),以便进行后续的流式处理。
2、收集到Map: tomap(键,值,替换),这三个参数。使用.collect(Collectors.toMap(...))方法将流中的元
   素收集到一个Map中。这个Map的键是通过一个函数生成的,该函数将每个CalculationDetailLogBatch对
   象的userName和accountId拼接成一个字符串(log.getUserName()+""+log.getAccountId()),而值
   则是流中的原始对象(Function.identity()表示“恒等函数”,即返回输入参数本身 就是输出的log对
   象)。
   源码:
       Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
                                    Function<? super T, ? extends U> valueMapper,
                                    BinaryOperator<U> mergeFunction) {
        return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
    }
3、处理键值冲突:由于Map的键必须是唯一的,如果两个CalculationDetailLogBatch对象具有相同的
  userName和accountId,则它们会在添加到Map时发生冲突。这里通过提供一个合并函数(o, n) -> o来解
  决这个冲突,这个函数表示如果键已存在(即已经有一个具有相同userName和accountId的对象在Map中),
  则保留旧的对象(o),忽略新的对象(n)。
4、从Map中提取值:通过.values()方法获取Map中所有的值(即去重后的CalculationDetailLogBatch对
   象),这些值现在是一个Collection<CalculationDetailLogBatch>类型的集合。
5、再次收集到List:最后,使用.stream().collect(Collectors.toList())将这个集合转换回一个列表
  (List),完成整个操作。
  
        List<CalculationDetailLogBatch> collect1 = logList.stream()
                .collect(Collectors.toMap(log -> log.getUserName()+""+log.getAccountId(),
                        Function.identity(), (o, n) -> o)).values().stream().collect(Collectors.toList());
  • 2、根据某个进行去重操作,返回该字段
源码:
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

两种写法:lambda 表达式:

        List<String> collect = caseInfos.stream().map(CaseInfo::getSecurityCode).distinct().collect(Collectors.toList());
        List<String> collect1 = caseInfos.stream().map(caseInfo -> {
            return caseInfo.getSecurityCode();
        }).distinct().collect(Collectors.toList());
  • 3、list集合 进行相加

   源码:
       IntStream mapToInt(ToIntFunction<? super T> mapper);
   示例:
   List<Integer> countList= new ArrayList<>();
   countList.add(1);
   countList.add(2);
   countList.stream().mapToInt(item->{return item;}).sum();
   countList.stream().mapToInt(Integer::intValue).sum();

filter

  • 1、根据条件过滤

代码解释:
1、流操作:importAmountCos.stream()将importAmountCos(一个List<ExcelImportAmountCo>类型
          的列表)转换为一个流(Stream),以便进行后续的流式处理。
2、过滤操作:.filter(imporAmountCo -> {...})是一个中间操作,它接受一个谓词(Predicate)作为参
          数。这个谓词是一个函数式接口的实现,它接收一个ExcelImportAmountCo类型的参数(在这里命
          名为imporAmountCo)并返回一个布尔值。流中的每个元素都会经过这个谓词的处理,只有当谓词返
          回true时,该元素才会被保留在流中继续传递。
          在这个例子中,谓词检查每个ExcelImportAmountCo对象的userName属性和accountId属性是否
          分别等于给定的userName和accountId变量。只有当这两个条件同时满足时,imporAmountCo才会
          被保留在流中。
3、收集操作:.collect(Collectors.toList())是一个终端操作,它表示流的结束。这个操作将流中的剩余元
            素收集到一个新的列表中。由于前面的过滤操作已经去除了不满足条件的元素,所以最终收集到的
            列表将只包含那些userName和accountId与给定值相匹配的ExcelImportAmountCo对象。

注意:变量名collect在这里可能有些误导,因为它实际上是一个列表(List),而不是一个收集器
     (Collector)。通常,我们会选择更具描述性的变量名,比如filteredImportAmountCos或类似的名
     称,以更清楚地表达这个列表的内容。

String userName="张三";
String accountId="123";

            List<ExcelImportAmountCo> collect = importAmountCos.stream().filter(imporAmountCo -> {
                return imporAmountCo.getUserName().equals(userName) && imporAmountCo.getAccountId().equals(accountId);
            }).collect(Collectors.toList());
  • 2、实体list(A)中嵌套一个实体list(B), 遍历出来B中包含 C(B中某个字段值的集合)。
//三条数据, 将user1List 中id=a0 检索出来。
[User(name=张三0, age=0, user1List=[User1(id=153, account=111222)]), 
User(name=张三1, age=0, user1List=[User1(id=a0, account=b0), User1(id=a1, account=b1)]),
User(name=张三2, age=0, user1List=[User1(id=153, account=111222)])]

1、初始集合:users是一个包含User对象的集合(List、Set等,但根据.stream()方法的调用,我们可以合理推测它是Collection或List类型)。

2、创建Stream:通过调用users.stream(),我们创建了一个Stream<User>,这是一个包含User对象的序列,支持顺序和并行聚合操作。

3、过滤操作:使用.filter(Predicate<? super T> predicate)方法对Stream中的元素进行过滤。这里的Predicate是一个函数式接口,
           它接受一个输入参数并返回一个布尔值结果。在这个例子中,Predicate是一个Lambda表达式,它对于Stream中的每个User对象
           都执行以下操作:

4、内层Stream:对于每个User对象,代码通过user.getUser1List().stream()创建了一个包含该用户的User1对象列表的Stream(假设
              User类有一个名为getUser1List的方法,该方法返回一个包含User1对象的集合或列表)。

5、内层过滤:在内层Stream上调用.anyMatch(Predicate<? super T> predicate)方法,这个方法检查Stream中是否存在至少一个元素满
           足给定的条件。这里的条件是通过另一个Lambda表达式指定的,它对于每个User1对象都检查其ID是否包含在set集合中(假设set
           是一个包含ID的集合,如Set<Integer>)。

6、返回结果:如果内层Stream中存在至少一个User1对象的IDset中,则外层Lambda表达式返回true,这意味着该User对象应该被包含在最
           终结果中。

7、收集结果:最后,使用.collect(Collectors.toList())方法将过滤后的Stream元素收集到一个新的列表中。这个列表包含了所有满足条
           件的User对象。
           
  List<User> list = users.stream().filter(user -> {
            return user.getUser1List().stream().anyMatch(user1 -> {
                return set.contains(user1.getId());
            });
        }).collect(Collectors.toList());

结果:

根据条件检索出来的结果---------->[User(name=张三1, age=0, user1List=[User1(id=a0, account=b0), User1(id=a1, account=b1)])]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值