java 1.8 Stream

  • stream:
    • 是数据渠道,用于操作数据源(数组,集合等.)所产生的元素序列
      • 集合讲的是数据,流讲的是计算.
      • 并行: parallel 使用并行流要注意线程安全问题.- Collections.synchronizedList
    • 注意:
      • stram 自己不会存储元素.
      • stream 不会改变源对象,相反 他们会返回一个持有结果的新stream.
      • stream 操作是延迟执行的,这意味着他们会等到需要结果的时候才执行.
    • 使用步骤:
      • 1.创建stream
      • 2.中间操作
      • 3.终止操作
        • 注意:中间操作 不会执行任何操作,只有在终止操作的时候才会一次性执行.
        • 多个中间操作可以连接起来形成一个流水线,除非流水线上出发终止操作,否则中间操作不会执行任何处理.而在终止操作时一次性全部处理.称为 "惰性求值".
    •     public static void main(String[] args) {
              //可以通过Collection系列集合提供的stream() 串行 或 parallelStream() 并行
              List<String> list = new ArrayList<>();
              Stream<String> stream = list.stream();
      
              //通过Arrays中的静态方法stream()获取 数组流
              LoginRequest[] loginRequests = new LoginRequest[10];
              Stream<LoginRequest> stream1 = Arrays.stream(loginRequests);
      
              //通过stream类中的静态方法of()
              Stream<String> aa = Stream.of("aa");
      
              //创建 无限流
                  //迭代
              Stream<Integer> iterate = Stream.iterate(0, (x) -> x + 2);
              //iterate.limit(10) 就是中间操作
              //forEach(System.out::println) 就是终止操作
              iterate.limit(10).forEach(System.out::println);
          }
      
      //并行流 parallelStream
          public static void main(String[] args) {
              List<Integer>  list  =  new ArrayList<>();
              for (int j = 0; j < 1000; j++) {
                  list.add(j);
              }
              System.out.println("最开始生成的集合长度:"+list.size());
              //parallelStream遍历数据的时候会产生丢失的问题
              for (int i = 0; i < 10 ; i++) {
       
                  List<Integer> parseList = new ArrayList<>();
                  List<Integer> synchronizedList = Collections.synchronizedList(parseList);
                  list.parallelStream().forEach(integer -> {
                      synchronizedList.add(integer);
                  });
                  System.out.println("每次遍历的集合长度:"+ synchronizedList.size());
              }
          }

    • 中间操作:

      • 筛选与切片

        • filter  接收lambda, 从六中排除某些元素

        • limit  截断流.使其元素不超过给定数量

        • skip(n) 跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit互补

        • distinct 筛选(去重),通过流所生成元素的hashCode()和equals()去除重复元素

      • filter  筛选:

        • static List<LoginRequest> loginRequests = Arrays.asList(
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("李四", "2222", "22"),
                      new LoginRequest("王五", "3333", "33"),
                      new LoginRequest("赵六", "6666", "66")
              );
          
              /**
               * @Description: filter  筛选
               * @Param: [args]
               * @return: void
               * @Author: 单人影
               * @Date: 2019/12/8 0008 15:59
               */
              public static void main(String[] args) {
                  Stream<LoginRequest> loginRequestStream = loginRequests.stream().filter((e) -> "22".equals(e.getAge()));
                  loginRequestStream.forEach(System.out::println);
              }

      • limit截断:

        •  static List<LoginRequest> loginRequests = Arrays.asList(
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("李四", "2222", "22"),
                      new LoginRequest("王五", "3333", "33"),
                      new LoginRequest("赵六", "6666", "66")
              );
          
              /**
               * @Description: limit截断
               * @Param: [args]
               * @return: void
               * @Author: 单人影
               * @Date: 2019/12/8 0008 15:59
               */
              public static void main(String[] args) {
                  loginRequests.stream()
                          .filter((e) -> Integer.valueOf(e.getAge()) > 11)
                          .limit(2)
                          .forEach(System.out::println);
              }
          
          控制台:
          LoginRequest{userName='李四', passWord='2222', age='22'}
          LoginRequest{userName='王五', passWord='3333', age='33'}

      • skip 跳过:

        •   static List<LoginRequest> loginRequests = Arrays.asList(
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("李四", "2222", "22"),
                      new LoginRequest("王五", "3333", "33"),
                      new LoginRequest("赵六", "6666", "66")
              );
          
              /**
               * @Description: skip 跳过
               * @Param: [args]
               * @return: void
               * @Author: 单人影
               * @Date: 2019/12/8 0008 15:59
               */
              public static void main(String[] args) {
                  loginRequests.stream()
                          .filter((e) -> Integer.valueOf(e.getAge()) > 11)
                          .skip(2)
                          .forEach(System.out::println);
              }
          
          控制台:
          LoginRequest{userName='赵六', passWord='6666', age='66'}

      • distinct 去重

        • static List<LoginRequest> loginRequests = Arrays.asList(
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("张三", "1111", "11"),
                      new LoginRequest("李四", "2222", "22"),
                      new LoginRequest("王五", "3333", "33"),
                      new LoginRequest("赵六", "6666", "66")
              );
          
              /**
               * @Description: distinct 去重
               * @Param: [args]
               * @return: void
               * @Author: 单人影
               * @Date: 2019/12/8 0008 15:59
               */
              public static void main(String[] args) {
                  loginRequests.stream()
                          .filter((e) -> Integer.valueOf(e.getAge()) > 1)
                          .distinct()
                          .forEach(System.out::println);
              }
          
          要重写对象的equals和hashCode方法.
          控制台:
          LoginRequest{userName='张三', passWord='1111', age='11'}
          LoginRequest{userName='李四', passWord='2222', age='22'}
          LoginRequest{userName='王五', passWord='3333', age='33'}
          LoginRequest{userName='赵六', passWord='6666', age='66'}

    • 映射:

      • map 接受lambda,讲元素转成其他形式或提取信息,接受一个函数作为参数,该函数会被应用到每一个元素上,并将其映射成一个新的元素.

      • flagMap 接收一个函数作为参数,将流中的每一个值都转换成另一个流,然后把所有的流了解成一个流.

      •     /**
             * @Description: 映射 map
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                List<String> list = Arrays.asList("aaa", "bbb", "ccc", "ddd");
                list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
            }
        
        控制台:
        AAA
        BBB
        CCC
        DDD
        
        
            /**
             * @Description: 映射 flatMap
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                list.stream().map((str) -> str.toUpperCase()).forEach(System.out::println);
                System.out.println("----------------------------------");
                //正常的map取值
                Stream<Stream<Character>> streamStream = list.stream().map(Test::filter);
                //使用flatMap 都放到flagMap中
                Stream<Character> characterStream = list.stream().flatMap(Test::filter);
                characterStream.forEach(System.out::println);
        
            }
        
            public static Stream<Character> filter(String string) {
                List<Character> list = new ArrayList();
                for (Character character : string.toCharArray()) {
                    list.add(character);
                }
                return list.stream();
            }
        
        总结:map相当于把一个个流加到map这个大流中.
            flatMap 相当于把流中的一个个元素加到flatMap流中

    • 排序:

      • sorted()  自然排序

      • sorted(Comparator com) 定制排序

      •  static List<LoginRequest> loginRequests = Arrays.asList(
                    new LoginRequest("张三", "1111", "11"),
                    new LoginRequest("张三1", "1113", "11"),
                    new LoginRequest("张三2", "1112", "11"),
                    new LoginRequest("李四", "2222", "22"),
                    new LoginRequest("王五", "3333", "33"),
                    new LoginRequest("赵六", "6666", "66")
            );
            static List<String> list = Arrays.asList("aaa", "ccc", "ddd", "bbb");
        
        
            /**
             * @Description: 排序 sorted
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                list.stream().sorted().forEach(System.out::println);
                //控制台 aaa bbb ccc ddd
                loginRequests.stream().sorted((x, y) -> {
                            //年龄相同 比密码
                            if (x.getAge().equals(y.getAge())) {
                                return x.getPassWord().compareTo(y.getPassWord());
                            } else {
                                //直接比年龄
                                return Integer.valueOf(x.getAge()).compareTo(Integer.valueOf(y.getAge()));
        //倒叙 return -Integer.valueOf(x.getAge()).compareTo(Integer.valueOf(y.getAge()));
                            }
                        }
                ).forEach(System.out::println);
            }
        
        控制台:
        LoginRequest{userName='张三', passWord='1111', age='11'}
        LoginRequest{userName='张三2', passWord='1112', age='11'}
        LoginRequest{userName='张三1', passWord='1113', age='11'}
        LoginRequest{userName='李四', passWord='2222', age='22'}
        LoginRequest{userName='王五', passWord='3333', age='33'}
        LoginRequest{userName='赵六', passWord='6666', age='66'}

    • 终止:

      • 查找与匹配:

        • allMatch 检查是否匹配所有元素

        • anyMatch 检查是否至少匹配一个元素

        • noneMatch 检查是否没有匹配的所有元素

        • findFirst 返回第一个元素

        • finaAny 返回当前流中的任意元素

        • count 返回流中元素的总个数

        • max 返回流中最大值

        • min 返回流中最小值

      •     static List<LoginRequest> loginRequests = Arrays.asList(
                    new LoginRequest("张三", "1111", "11"),
                    new LoginRequest("张三1", "1113", "11"),
                    new LoginRequest("张三2", "1112", "11"),
                    new LoginRequest("李四", "2222", "22"),
                    new LoginRequest("王五", "3333", "33"),
                    new LoginRequest("赵六", "6666", "66")
            );
        
            /**
             * @Description: 查找匹配
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                boolean b = loginRequests.stream().allMatch((x) -> x.getPassWord().equals("111111"));
                System.out.println(b);
                //其余的匹配用法基本相同
        
                //findFirst
                Optional<LoginRequest> optional = loginRequests.stream().sorted((x, y) -> x.getAge().compareTo(y.getAge())).findFirst();
                //Optional是为了放置空指针 如果为空就用orElse()括号里面的.
                LoginRequest loginRequest1 = new LoginRequest("111", "222", "333");
                LoginRequest loginRequest = optional.orElse(loginRequest1);
                System.out.println(loginRequest.toString());
        
            }
        
        控制台:
        false
        LoginRequest{userName='张三', passWord='1111', age='11'}

    • 归约 reduce(T identity,BinaryOperator ) /  reduct(BinaryOperator)  可以将流中元素反复结合起来,得到一个值.

      •     /**
             * @Description: 归约 reduce
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
                Integer reduce = list.stream().reduce(0, (x, y) -> x + y);
                System.out.println(reduce);
                System.out.println("================================");
                //有可能返回为空的都是Optional ,因为上面指定了起始值.不可能为空.
                Optional<String> reduce1 = loginRequests.stream().map(LoginRequest::getUserName).reduce((x, y) -> x += (y));
                System.out.println(reduce1);
            }
        
        控制台:
        15
        ================================
        Optional[张三张三1张三2李四王五赵六]

    • 收集: collect 将流转换为其他形式,接受一个collector接口的实现,用于给stream中元素做汇总的方法. (分组)

      •     static List<LoginRequest> loginRequests = Arrays.asList(
                    new LoginRequest("张三", "1111", "11"),
                    new LoginRequest("张三1", "1113", "11"),
                    new LoginRequest("张三2", "1112", "11"),
                    new LoginRequest("李四", "2222", "22"),
                    new LoginRequest("王五", "3333", "33"),
                    new LoginRequest("赵六", "6666", "66")
            );
            static List<String> list = Arrays.asList("aaa", "ccc", "ddd", "bbb");
        
        
            /**
             * @Description: 收集 collect
             * @Param: [args]
             * @return: void
             * @Author: 单人影
             * @Date: 2019/12/8 0008 15:59
             */
            public static void main(String[] args) {
                List<String> collect = loginRequests.stream().map(LoginRequest::getUserName).collect(Collectors.toList());
                System.out.println(collect.toString());
                Set<String> collects= loginRequests.stream().map(LoginRequest::getAge).collect(Collectors.toSet());
                System.out.println(collects.toString());
                //分组 groupingBy  还可以多级分组
                Map<String, List<LoginRequest>> collect1 = loginRequests.stream().collect(Collectors.groupingBy(LoginRequest::getAge));
                System.out.println(collect1.toString());
                //还可以分区.true一个区 false一个区 Collectors.partitioningBy
                //连接 Collectors.join()      可以写成join(",") 逗号拼接
               
            }
        
        控制台:
        [张三, 张三1, 张三2, 李四, 王五, 赵六]
        [11, 22, 33, 66]
        {66=[LoginRequest{userName='赵六', passWord='6666', age='66'}], 33=[LoginRequest{userName='王五', passWord='3333', age='33'}], 22=[LoginRequest{userName='李四', passWord='2222', age='22'}], 11=[LoginRequest{userName='张三', passWord='1111', age='11'}, LoginRequest{userName='张三1', passWord='1113', age='11'}, LoginRequest{userName='张三2', passWord='1112', age='11'}]}
         
  • 使用示例:
    • list累加
      BigDecimal allAssets = tradeBalanceCrmList.stream().filter(item -> item.getTotalProfit() != null).map(TradeBalanceCrmDto::getTotalProfit).reduce(BigDecimal.ZERO, BigDecimal::add);
      
      分组
      Map<String, List<TradeBalanceCrmDto>> productTypeGroupBy = tradeBalanceCrmList.stream().filter(item -> StringUtils.isNotEmpty(item.getProductType())).collect(Collectors.groupingBy(TradeBalanceCrmDto::getProductType));
      
      遍历
      for (String key : productTypeGroupBy.keySet()) {
                 if (ProductTypeEnum.PUBLICFUND.getCode().equals(key)) {
                      publicList.addAll(productTypeGroupBy.get(key));
                      log.debug("公募:{},value:{}", productTypeGroupBy.get(key));
                  } else if (ProductTypeEnum.PRIVATEFUND.getCode().equals(key)) {
                      privateList.addAll(productTypeGroupBy.get(key));
                      log.debug("似募:{},value:{}", productTypeGroupBy.get(key));
                  }
              }
      
      排序:
       Collections.sort(tradeAppFlowDtos, (o1, o2) -> o2.getAcceptTime().compareTo(o1.getAcceptTime()));
      
      如果要按照升序排序,
      则o1 小于o2,返回-1(负数),相等返回0,01大于02返回1(正数)
      如果要按照降序排序
      则o1 小于o2,返回1(正数),相等返回0,01大于02返回-1(负数)
      
      
      去除为空字段.返回list
      List<CustManagerInfo> collect = custManagerInfos.stream().filter((item) -> StringUtils.isNotEmpty(item.getFundManagerCode())).collect(Collectors.toList());

    • 双层for循环:

      •  List<TradeBalanceCrmDto> tradeBalanceCrmDtos = tradeBalanceCrmExtendMapper.selectByConditionByCustId(custId);
                if (CollectionUtils.isEmpty(tradeBalanceCrmDtos)) {
                    return tradeBalanceCrmDtos;
                }
                List<TradeBalanceCrmDto> list = new ArrayList<>();
                TradeBalanceCrmDto tradeBalanceCrmDtoResult = null;
                for (TradeBalanceCrmDto tradeBalanceCrmDto : tradeBalanceCrmDtos) {
                    boolean flag = list.stream().anyMatch(item -> item.getFundCode().equals(tradeBalanceCrmDto.getFundCode()));
                    if (flag) {
                        continue;
                    }
                    tradeBalanceCrmDtoResult = new TradeBalanceCrmDto();
                    BeanUtils.copyProperties(tradeBalanceCrmDto, tradeBalanceCrmDtoResult);
                    List<TradeBalanceCrmDto> collect = tradeBalanceCrmDtos.stream().filter((item) -> tradeBalanceCrmDto.getFundCode().equals(item.getFundCode())).collect(Collectors.toList());
                    log.debug("归集的List为:{}", collect.toString());
                    tradeBalanceCrmDtoResult.setFundAsset(collect.stream().filter((item) -> item.getFundAsset() != null).map(TradeBalanceCrmDto::getFundAsset).reduce(BigDecimal.ZERO, (x, y) -> x.add(y)));
                    tradeBalanceCrmDtoResult.setTotalBala(collect.stream().filter((item) -> item.getTotalBala() != null).map(TradeBalanceCrmDto::getTotalBala).reduce(BigDecimal.ZERO, (x, y) -> x.add(y)));
                    tradeBalanceCrmDtoResult.setAvailBala(collect.stream().filter((item) -> item.getAvailBala() != null).map(TradeBalanceCrmDto::getAvailBala).reduce(BigDecimal.ZERO, (x, y) -> x.add(y)));
                    tradeBalanceCrmDtoResult.setTotalProfit(collect.stream().filter((item) -> item.getTotalProfit() != null).map(TradeBalanceCrmDto::getTotalProfit).reduce(BigDecimal.ZERO, (x, y) -> x.add(y)));
                    list.add(tradeBalanceCrmDtoResult);
                }

  • list 大批量数据处理 分组

  • // 1.java8 Stream 大数据量List分批处理
    
    //按每50个一组分割
    private static final Integer MAX_NUMBER = 2;
    
    /**
    * 计算切分次数
    */
    private static Integer countStep(Integer size) {
    	return (size + MAX_NUMBER - 1) / MAX_NUMBER;
    }
    
    public static void main(String[] args) {
          List<String> list = Arrays.asList('1', '2', '3', '4', '5', '6','7');
          int limit = countStep(list.size());
          //方法一:使用流遍历操作
          List<List<String>> mglist = new ArrayList<>();
          Stream.iterate(0, n -> n + 1).limit(limit).forEach(i -> {
              mglist.add(list.stream().skip(i * MAX_NUMBER).limit(MAX_NUMBER).collect(Collectors.toList()));
          });
    
          System.out.println(mglist);
    
          //方法二:获取分割后的集合
          List<List<String>> splitList = Stream.iterate(0, n -> n + 1).limit(limit).parallel().map(a -> list.stream().skip(a * MAX_NUMBER).limit(MAX_NUMBER).parallel().collect(Collectors.toList())).collect(Collectors.toList());
          
          System.out.println(splitList);
    }
    
    // 2.使用google guava对List进行分割
    //按每50个一组分割
    List<List<String>> parts = Lists.partition(list , 50);
     
    
    // 3.使用apache common collection
    List<Integer> intList = Lists.newArrayList(1, 2, 3, 4, 5, 6, 7, 8);
    List<List<Integer>> subs = ListUtils.partition(intList, 3);
    
    // 4.手写代码算
    public static <T> List<List<T>> averageAssign(List<T> source, int n) {
        List<List<T>> result = new ArrayList<>();
    	//(先计算出余数)
        int remainder = source.size() % n;  
        //然后是商
        int number = source.size() / n;  
    	//偏移量
        int offset = 0;
        for (int i = 0; i < n; i++) {
            List<T> value;
            if (remainder > 0) {
                value = source.subList(i * number + offset, (i + 1) * number + offset + 1);
                remainder--;
                offset++;
            } else {
                value = source.subList(i * number + offset, (i + 1) * number + offset);
            }
            result.add(value);
        }
        return result;
    }
    

  • 常用实例:

    • //过滤不为空的list
      List<CustManagerInfo> collect = custManagerInfos.stream().filter((item) -> 
      StringUtils.isNotEmpty(item.getFundManagerCode())).collect(Collectors.toList());
      //非空 分组
      Map<String, List<TransferListVo>> registarCodeMap = list.stream().filter(item -> StringUtils.isNotEmpty(item.getRegistrarCode())).collect(Collectors.groupingBy(TransferListVo::getRegistrarCode));
      //非空 累加
      BigDecimal allAssets = tradeBalanceCrmList.stream().filter(item -> item.getTotalProfit() != null).map(TradeBalanceCrmDto::getTotalProfit).reduce(BigDecimal.ZERO, BigDecimal::add);
      
      //取对象的某个字段 拼接成字符串
      String fundCodes = responseList.stream().map(r -> r.getFundCode()).collect(Collectors.joining(","));
      
      //字符串逗号份额 变成list
      List<String> collect2 = Arrays.stream(fundCodes.split(",")).collect(Collectors.toList());
      
      //list排序
      response.getStateList().sort(Comparator.comparingInt(o -> (Integer.parseInt((o.getCustNo())))));
      
      //分批处理
      /**
      * 分批次查询 50条一次
      */
      private final int MAX_NUMBER = 50;
      List<List<String>> partition = Lists.partition(fundCodes, MAX_NUMBER);
      
      //正序
      Collections.sort(this.arrayList);
      //倒叙
      Collections.sort(this.arrayList, Collections.reverseOrder());
      
      //过滤
      List<ImportIaFundInfoVo> filterList = importIaFundInfoVos.stream().filter((item) -> StringUtils.isNotEmpty(item.getFundCode())).collect(Collectors.toList());
      
      // importIaFundInfoVos 根据 基金代码 去重
      List<ImportIaFundInfoVo> collect = filterList.stream().collect(collectingAndThen(toCollection(() -> new TreeSet<>(Comparator.comparing(ImportIaFundInfoVo::getFundCode))), ArrayList::new));
      
      // anyMatch
      boolean aa = strs.stream().anyMatch(str -> str.equals("a"));
      
      // 对象属性逗号拼接 去重     
      iaClientRequest.setCombineManagers(pageInfo.getList().stream().map(IaSignFlow::getChannelType).distinct().collect(Collectors.joining(",")));
      
      //list 转list 然后根据某个主键 变成Map<String, IaClientConfigDTO> 主键对象的形式
      Map<String, IaClientConfigDTO> collect = BeanUtil.copyListProperties(list, IaClientConfigDTO::new).stream().collect(Collectors.toMap(IaClientConfigDTO::getClientId, Function.identity(), (key1, key2) -> key2));
      
      // 字符串过滤拼接
              String tradeAccos = batchContractFlows.stream().map(BatchContractFlow::getTradeAcco)
                      .filter(StringUtils::isNotEmpty).distinct().collect(Collectors.joining(","));
      
              Map<String, CustomerEstablishAccountDTO> fofundNoMap = records.stream()
                      .filter(item -> StringUtils.isNotEmpty(item.getFofundNo()))
                      .collect(Collectors.toMap(CustomerEstablishAccountDTO::getFofundNo, Function.identity(), (key1, key2) -> key2));
      
      //list按照某些属性 从另一个list中过滤不存在的数据
                  List<TradeConfirmUqKeyBO> noConfirm = params.getConfirmUqKey().stream().
                          filter(item -> confirmList.stream()
                                  .noneMatch(confirm -> StringUtils.equals(item.getConfirmNo(), confirm.getConfirmNo()) &&
                                          StringUtils.equals(item.getFundCode(), confirm.getFundCode()) &&
                                          StringUtils.equals(item.getConfirmDate(), confirm.getConfirmDate())))
                          .collect(Collectors.toList());
      
      // list变map key多字段拼接
              Map<String, TestData> map = list.stream().collect(Collectors.toMap(d -> d.getName() + "-" + d.getSex(), Function.identity(),(d1,d2) -> d1));
      
      // list 两个字段合并到一个 list中
              List<String> fundCodeList = pageDataOfPage.getRecords().stream().flatMap(d -> Stream.of(d.getFundCode(), d.getTargetFundCode())).distinct().collect(Collectors.toList());
      
      
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值