Stream流

Stream流

1 Stream流的概述

在处理集合的时候,通常我们都会遍历其中的元素,然后对每一个元素进行处理,例如:统计List列表中字符长度大于8的元素个数。

int count = 0;
for(String str : list){
    if(str.length() > 8)count++;
}

在java8开始,可以使用Stream流来对集合进行操作:

long count = list.stream().filter(str -> str.length() > 8).count();

使用Stream流,从方法名就可以看出代码逻辑:过滤字符长度大于8的字符并统计个数。

1.1 流的特点

  • 流并不存储元素
  • 流的操作不会修改数据源。filter()方法并不会将元素从数据源中删除,而是会生成一个新的流
  • 流的操作是惰性的

1.2 流的使用过程

  1. 创建一个流:list.stream()
  2. 指定将初始流转换为其他流的中间操作:filter(str -> str.length() > 8)
  3. 应用终止操作:count()

2 Stream流的创建

2.1 通过Collection的Stream()创建流

Collection接口中含有一个Stream()方法,该方法可以通过集合创建一个流,这是比较常用的方式。

public class TestStream {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> setStream = set.stream();

        Queue<String> queue = new ArrayDeque<>();
        Stream<String> queueStream = queue.stream();

    }
}

2.2 通过Stream类的静态方法创建流

  • static Stream of(T… values)

    通过给定元素创建一个流

     Stream<String> stream = Stream.of("Hello", "java");
    
  • static Stream empty()

    创建一个不包含任何元素的流

    Stream<Object> emptyStream = Stream.empty();
    
  • static Stream gengerate(Supplier s)

    创建一个无限流,流的值通过反复调用方法s获得

    Stream<Double> randomStream = Stream.generate(() -> Math.random());
    
  • static Stream iterate(T seed,UnaryOperator f)

    创建一个无限流,流的第一个元素为seed,之后的每一个元素通过调用方法f并将seed作为入参得到

     Stream<Integer> integerStream = Stream.iterate(100, seed -> seed + 1);
    
  • static Stram iterate(T seed,Predicate<? super T> hasNext,UnaryOperator f)

    创建一个无限流,流的第一个元素为sedd,之后的每一个元素通过调用方法f并将seed作为入参得到,当出现第一个不满hasNext的判断时终止创建

     Stream<Integer> integerStream = Stream.iterate(100,item -> item <= 200, seed -> seed + 1);
    
  • static Stream ofNullable(T t)

    如果t为null,返回一个空流,否则返回包含t的流

    Stream<String> stram = Stream.ofNullable("Java")
    

2.3 通过Arrays的stream()方法创建

  • static Stream stream(T[] array)

    创建一个流,需要传入一个数组

    Stream<String> stream = Arrays.stream(new String[]{"hello", "java"});
    

2.4 通过Files的lines()方法创建

  • static Stream lines(Path path)

    创建一个流,流的元素就是文件中的行

     Stream<String> stram = Files.lines(Paths.get("../data/HelloJava.txt"));
    

3 Stream流的中间操作

流的中间操作会产生一个新的流,它的元素派生自另一个流中的元素

3.1 filter–过滤

  • Stream filter (Predicate<? super T> predicate)

    传入一个断言函数,返回一个包含满足断言条件的所有元素的流。

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Stream<String> stream = Stream.of("Hello", "java","hello","world");
            // 筛选出字符长度<=4的元素
            Stream<String> newStream = stream.filter(s -> s.length() <= 4);
        }
    }
    

3.2 map–映射

  • Stream map (Function<? super T,? extend Stream<? extend R>> mapper)

    产生一个新的流,它将包含mapper应用于当前流中所有元素所产生的的结果

    	Stream<String> stream = Stream.of("Hello", "java","hello","world","Spring","Mysql");
    	// 将流中的元素变成大写
    	Stream<String> upStream = stream.map(s -> s.toUpperCase());
    	// 得到每个元素的长度的流
    	Stream<Integer> lengthStream = stream.map(s -> s.length());
    

3.3 flatMap–摊平

  • Stream flatMap(Function<? super T,? extends Stream<? extends R>> mapper)

    产生一个流,它是通过将mapper应用于当前流中所有元素所产生的的结果链接到一起而获得。通俗地讲就是:如果流中的元素经过mapper处理后得到的是流,那么将这些流拆开,把里面的元素提取出来,摊平成一个新的流。例如:

    “H/e/l/l/o”经过splitString方法处理后得到的是流[“H”,“e”,“l”,“l”,“o”];那么oldStream中的元素全部经过splitString方法处理后得到是[[“H”,“e”,“l”,“l”,“o”],[“J”,“a”,“v”,“a”]],用flatMap摊平之后的流[“H”,“e”,“l”,“l”,“o”,“J”,“a”,“v”,“a”]

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Stream<String> oldStream = Stream.of("H/e/l/l/o", "j/a/v/a");
            Stream<String> newStream = stream.flatMap(s -> splitString(s));
        }
    
        private static Stream<String> splitString(String str){
            String[] split = str.split("/");
            Stream<String> stream = Arrays.stream(split);
            return stream;
        }
    }
    

3.4 limit–抽取

  • Stream limit(long maxSize)

    产生一个流,其中包含了当前流中最初的maxSize个元素

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Stream<String> stream = Stream.of("h","e","l","l","o");
            // 新的流中只含有三个元素:h e l
            Stream<String> newStream = stream.limit(3);
             // 产生5个随机数流
          	Stream<Double> stream = Stream.generate(Math::random).limit(5);
        }
    }
    
    

3.5 skip–跳过

  • Stream skip(long n)

    产生一个流,它的元素是当前流中除了前n个元素之外的所有元素

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Stream<String> stream = Stream.of("h","e","l","l","o");
            // 新的流中只含有两个元素:l o
            Stream<String> newStream = stream.skip(3);
        }
    }
    

3.6 distinct–去重

  • Stream<T>distinct()

    将流中的重复元素剔除,保证流中的所有元素是不重复的

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Stream<String> stream = Stream.of("h","e","l","l","o");
            // 新的流中元素为:h e l o,剔除了一个重复的l
            Stream<String> newStream = stream.distinct();
        }
    }
    

3.7 sorted–排序

  • Stream<T> sorted()

  • Stream<T>sorted(Compartor<? super T> compartor)

    产生一个新的流,它的元素是当前流中的所有与元素按照顺序排列的。第一个方法要求元素是实现了Comparable类的实例

    public class TestStream {
        public static void main(String[] args) throws IOException {
            // 流中的元素排列顺序为:2 3 4 7 8 9
            Stream<Integer> integerStream = Stream.of(3, 2, 9, 4, 7, 8).sorted();
            // 流中的元素按照降序排列:9 8 7 4 3 2
            Stream<Integer> integerStream2 = Stream.of(3, 2, 9, 4, 7, 8).sorted(Comparator.comparing(Integer::intValue).reversed());
        }
    }
    

3.8 peek

  • Stream<T> peek(Consumer<? super T> action)

    产生一个流,它与当前流中的相同,在获取其中每个元素时,会将器传递给action;这个方法可以来窥探流中的元素

    public class TestStream {
        public static void main(String[] args) throws IOException {
            Object[] objects = Stream.of(1, 3, 5, 8, 9, 5)
                    .filter(item -> item >= 5)
                    .peek(System.out::print) // 它会将经过过滤后还剩的元素进行打印
                    .limit(9)
                    .toArray();
            System.out.println(objects.toString());
    
        }
    }
    
    

4 Stream流的终结操作

流的终结操作将不会再返回流,而是将流中的元素处理成一个结果。

4.1 max,min–最大最小

  • Optional<T> max(Comparator<? super T> compartor)

  • Optional<T> min(Comparator<? super T> compartor)

    分别获得这个流中的最大最小元素,如果流是空的,那么会产生一个空的Optional对象。

public class TestStream {
    public static void main(String[] args) throws IOException {
        Integer max = Stream.of(1, 3, 5, 2, 7, 80, 0).max(Comparator.comparing(Integer::intValue)).get();
        System.out.println(max);// 输出 80

        Integer min = Stream.of(-3, 3, 5, 2, 7, 80, 0).min(Comparator.comparing(Integer::intValue)).get();
        System.out.println(min);// 输出-3
    }
}

4.2 findFirst,findAny

  • Optional<T> findFirst()

  • OPtional<T> findAny()

    分别产生这个流的第一个和任意一个元素,如果这个流为空,会产生一个空的Optional对象

public class TestStream {
    public static void main(String[] args) throws IOException {
        Integer first = Stream.of(1, 3, 5, 2, 7, 80, 0).filter(item -> item >= 5).findFirst().get();
        System.out.println(first);// 输出5

        Integer integer = Stream.of(9, 3, 5, 2, 7, 80, 0).findAny().get();
        System.out.println(integer);// 输出任意一个元素
    }
}

4.3 anyMatch,allMatch,noneMatch

  • boolean anyMatch(Predicate<? super T>predicate)

  • boolean allMatch(Predicate<? super T> predicate)

  • boolean noneMatch(Predicate<? super T> predicate)

    用给定的断言条件去匹配这个流中的元素。匹配到任意一个元素返回true,所有元素都满足条件返回true,没有元素满足条件返回true

public class TestStream {
    public static void main(String[] args) throws IOException {
        boolean result = Stream.of(1, 3, 5, 2, 7, 80, 0).anyMatch(item -> item > 9);
        System.out.println(result);// true
        boolean result2 = Stream.of(1, 3, 5, 2, 7, 80, 0).allMatch(item -> item > 9);
        System.out.println(result2);// false
        boolean result3 = Stream.of(1, 3, 5, 2, 7, 80, 0).noneMatch(item -> item > 0);
        System.out.println(result3);// false
    }
}

4.4 foreach–遍历

  • void forEach(Consumer<? super T> action)

    在流的每个元素上调用action。通常用来遍历流中的元素

public class TestStream {
    public static void main(String[] args) throws IOException {
        Stream.of(1, 3, 5, 2, 7, 80, 0).forEach(item -> System.out.println(item));
    }
}

4.5 toArray

  • Object[] toArray()

  • <A> A[] toArray(IntFunction<A[]> generator)

    产生一个对象数组,默认是Object[],如果想要具体的数组类型,列如String[],可以传入:String[]::new

public class TestStream {
    public static void main(String[] args) throws IOException {
        Object[] objects = Stream.of(1, 3, 5, 2, 7, 80, 0).toArray();
        Integer[] integers = Stream.of(1, 3, 5, 2, 7, 80, 0).toArray(Integer[]::new);
    }
}

4.6 reduce–约简

reduce约简,就是将流中的多个元素按照一定的规则简化成一个结果。

  • Optional<T>reduce(BinaryOperator<T> accumulator)

  • T reduce(T identity,BinaryOperator<T> accumulator)

  • U reduce (U identity,BiFunction<U,?superT,U)accumulator,BinaryOperator<U>combiner)

    用给定的accumulator函数产生流中元素的累积总和。如果提供了幺元,那么第一个被累积的元素就是幺元。如果提供了组合器,那么他可以用来将分别累积的各个部分整合成总和

public class TestStream {
    public static void main(String[] args) throws IOException {

        // 将流中的元素累加
        Optional<Integer> result = Stream.of(1, 2, 3, 4).reduce((x, y) -> x + y);
        // 用初始值10去累加流中的元素
        Integer result2 = Stream.of(1, 2, 3, 4).reduce(10, (x, y) -> x + y);
    }
}

5 collect–收集结果

  • <R,A> R collect(Collector<? super T,A,R> collector)

    使用给定的收集器来收集当前流中的元素,Collectors类有多种收集器的工厂方法。此方法比较常用

public class TestStream {
    public static void main(String[] args) throws IOException {
        List<String> list = getCityList().stream()
                .map(city -> city.getCityName())
                .collect(Collectors.toList());// 将城市名收集为一个list
        list.iterator().forEachRemaining(item -> System.out.println(item));
    }

    public static  List<City> getCityList(){
        return new ArrayList<City>(){{
            add(new City("成都","四川省",1800));
            add(new City("泸州","四川省",600));
            add(new City("广州","广东省",1800));
            add(new City("深圳","广东省",1900));
            add(new City("佛山","广东省",1000));
            add(new City("西安","陕西省",1000));
        }};
    }

    public static class City{
        private String cityName;
        private String province;
        private Integer peopleCount;
		...省略构造和getter,setter...
    }
}

5.1 java.util.Steram.Collectors

  • static <T> Collector<T,?,List<T> toList()

  • static <T> Collector<T,?,Set<T> toSet()

    产生一个将元素收集到列表或集合中的收集器

public class TestStream {
    public static void main(String[] args) throws IOException {
        Set<String> set = getCityList().stream()
                .map(city -> city.getProvince())
                .collect(Collectors.toSet());// 将省份收集为一个Set
        set.iterator().forEachRemaining(item -> System.out.println(item));
    }
}
  • static <T,C extends Collection> Collector<T,?,C> toCollection(Supplier<C>CollectionFactory)

    产生一个将元素收集到任意集合中的收集器,可以传一个诸如TreeSet::new的构造器引用

public class TestStream {
    public static void main(String[] args) throws IOException {
        TreeSet<Integer> treeSet = getCityList().stream()
                .map(city -> city.getPeopleCount())
                .collect(Collectors.toCollection(TreeSet::new));// 收集为指定的类型TreeSet
        treeSet.iterator().forEachRemaining(item -> System.out.println(item));
    }
}
  • static Collector<CharSequence,?,String> joining()

  • static Collector<CharSequence,?,String> joining(CharSequence delimiter)

  • static Collector<CharSequence,?,String> joining(CharSequence delimiter,CharSequence prefix,CharSequence suffix)

    产生一个字符串的收集器。可以传入链接字符串的分隔符,还可以在第一个字符串之前增加前缀,在最后一个字符串增加后缀

public class TestStream {
    public static void main(String[] args) throws IOException {
        String cityName = getCityList().stream()
                .map(city -> city.getCityName())
                .collect(Collectors.joining());
        System.out.println(cityName);// 成都泸州广州深圳佛山西安

        String cityName2 = getCityList().stream()
                .map(city -> city.getCityName())
                .collect(Collectors.joining("-"));
        System.out.println(cityName2);// 成都-泸州-广州-深圳-佛山-西安

        String cityName3 = getCityList().stream()
                .map(city -> city.getCityName())
                .collect(Collectors.joining("-","城市名:","..."));
        System.out.println(cityName3);// 城市名:成都-泸州-广州-深圳-佛山-西安...
    }
}
  • static <T> Collector<T,?,IntSummaryStatistics> summarizingInt(ToIntFunction<? super T> mapper>

  • static <T> Collector<T,?,LongSummaryStatistics> summarizingLong(ToLongFunction<? super T> mapper>

  • static <T> Collector<T,?,DoubleSummaryStatistics> summarizingDouble(ToDoubleFunction<? super T> mapper>

    产生能够生成(Int|Long|Double)SummaryStatistics对象的收集器,通过它们可以获得将mapper应用于每个元素后产生的结果的数量,综合,平均值,最大值,最小值

public class TestStream {
    public static void main(String[] args) throws IOException {
        IntSummaryStatistics summaryInt = getCityList().stream()
                .collect(Collectors.summarizingInt(City::getPeopleCount));
        System.out.println(summaryInt.getMax());//1900
        System.out.println(summaryInt.getAverage());//1350.0
        System.out.println(summaryInt.getCount());//6
        System.out.println(summaryInt.getMin());//600
        System.out.println(summaryInt.getSum());//8100
    }
}
  • static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper)

  • static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper,BinayOperator<U>mergeFunction)

  • static <T,K,U> Collector <T,?,Map<K,U>> toMap(Function<? super T,? extends K> keyMapper,Funciton<? super T,? extends U) valueMapper,BinayOperator<U>mergeFunction,Supplier<M>mapSupplier)

    产生一个收集器,收集为一个Map,keyMapper函数用来从元素中获取键,valueMapper函数的返回作为值。默认情况下,当两个元素产生相同的键时,会抛出一个IllegalStateException异常。可以提供一个mergerFunction来合并具有相同键的值。

public class TestStream {
    public static void main(String[] args) throws IOException {
        // key是cityName;value是city
        Map<String, City> map1 = getCityList().stream()
                .collect(Collectors.toMap(City::getCityName, Function.identity()));
        // key是cityNmae;value是peopleCount
        Map<String, Integer> map2 = getCityList().stream()
                .collect(Collectors.toMap(City::getCityName, City::getPeopleCount));
        // key是province;value是peopleCount;因为省份有重复,会抛异常
        Map<String, Integer> map3 = getCityList().stream()
                .collect(Collectors.toMap(City::getProvince, City::getPeopleCount));
        // key是province;value是peopleCount;当省份重复时,将它们的值累加
        Map<String, Integer> map4 = getCityList().stream()
                .collect(Collectors.toMap(City::getProvince, City::getPeopleCount, (existValue, newValue) -> existValue + newValue));
        
    }
}
5.1.1 Collectors.groupingBy 群组和分区
  • static <T,K> Collector<T,?,Map<K,List<T>> groupingBy(Function<? super T,? extends K> classifer)

    产生一个收集器,它会产生一个Map,键是将classifier应用于所有收集到的元素上所产生的结果,而值是具有相同的元素构成的一个个列表

public class TestStream {
    public static void main(String[] args) throws IOException {
        // 按照省份将city分组
        Map<String, List<City>> map = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince));
    }
}
  • static <T> Collector<T,?,Map<Boolean,List> partioningBy(predicate<? super T> predicate)

    产生一个收集器,它会产生一个Map,键是true/false,值是满足/不满足的元素列表

public class TestStream {
    public static void main(String[] args) throws IOException {
        // 将城市按照人数是否超过1000万分组
        Map<Boolean, List<City>> map2 = getCityList().stream()
                .collect(Collectors.partitioningBy(city -> city.getPeopleCount() >= 1000));
        System.out.println(map2);

    }
}
5.1.2 下游收集器

因为Collectors.groupingBy 的结果是Map,Map的值是一个列表List<T>,这个List<T>中的元素还可以应用收集器,例如收集列表List<T>中元素的最大值,最小值,统计个数等等。

  • static <T,K,A,D>Collector<T,?,Map<K,D>> groupingBy(Function<? super T,? extends K> classfier,Collector<? super T,A,D) downStream)

    产生一个收集器,收集结果是Map,Map的键是将classfier应用到所有收集到的元素上之后产生的结果,值是使用下游收集器具有相同的键的元素所产生的结果。通俗理解就是,stream流中的元素,先按classfier分组;得到结果Map<k,List<T>>,然后再将downStream应用到List<T>,产生的结果作为Map的值。

public class TestStream {
    public static void main(String[] args) throws IOException {

        // 将城市按省份分组,然后统计每个省份的城市数量
        Map<String, Long> map = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.counting()));
        System.out.println(map);// {陕西省=1, 广东省=3, 四川省=2}

        // 将城市分组,然后取每个分组中人口数最大的城市
        Map<String, Optional<City>> map2 = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.maxBy(Comparator.comparing(City::getPeopleCount))));
        System.out.println(map2);

        Map<String, Set<City>> map3 = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.toSet()));
        System.out.println(map3);

        // 将城市分组统计分组后城市人口数的平均值,最大,最小值等
        Map<String, IntSummaryStatistics> map4 = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.summarizingInt(City::getPeopleCount)));
        System.out.println(map4);

        // 将城市按省份分组,然后取每个分组中的城市名,并将城市名用"-"拼接
        Map<String, String> map5 = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.mapping(City::getCityName, Collectors.joining("-"))));
        System.out.println(map5);// {陕西省=西安, 广东省=广州-深圳-佛山, 四川省=成都-泸州}

        // 将城市按省份分组,然后统计每个组中的城市人口总和
        Map<String, Integer> map6 = getCityList().stream()
                .collect(Collectors.groupingBy(City::getProvince, Collectors.reducing(0, City::getPeopleCount, Integer::sum)));
        System.out.println(map6);// {陕西省=1000, 广东省=4700, 四川省=2400}

    }
}
<think>好的,用户想要找关于Java Stream的练习题和学习资料。首先,我需要回忆一下相关的知识点,并结合提供的引用内容来整理答案。 首先,用户提供的引用中有四个关于Stream的资料,其中提到了练习题、示例代码以及基础教程。比如引用3提到StreamJava 8的重要特性,允许声明式处理集合,这可能适合作为教程部分的推荐。引用1和引用2给出了具体的练习题,如转换并行、处理绝对值、使用flatMap拆分单词等。引用4则展示了过滤偶数的例子,这些都是很好的练习题素材。 接下来,我需要将这些内容分类整理,可能分为基础概念、练习题推荐和学习资源。同时,用户可能需要教程链接或书籍推荐,但根据引用中没有提供外部链接,可能需要建议官方文档或知名网站。 还要注意用户提到的系统级指令,比如数学公式的格式,但这次问题不涉及数学内容,所以可能不需要处理这部分。另外,生成相关问题的时候,要确保问题与Stream相关,并且基于已有的引用内容。例如,如何优化Stream性能、并行的使用场景、map和filter的区别等。 需要检查是否有遗漏的要点,比如用户可能对Stream的中间操作和终端操作有疑问,或者想了解常见的陷阱。所以在相关问题里可以包括这些方面。 最后,确保回答结构清晰,分点列出,并且每个部分都有对应的引用支持。例如,在练习题部分具体列出每个练习并标注引用来源,这样用户能清楚知道每个例子出自哪里。同时,学习资源部分可以推荐官方文档和书籍,如《Java 8实战》。 总结一下,回答应该包括基础概念、练习题示例、学习资源和相关问题,确保每个部分都有引用支持,并且语言自然畅。</think>### Java Stream学习资源与练习题整理 #### 一、Stream基础概念 StreamJava 8的核心特性之一,允许以声明性方式处理集合数据,支持函数式编程风格。例如: ```java list.stream().filter(n -> n % 2 == 0).forEach(System.out::println); ``` 这段代码通过`filter`筛选偶数并输出[^4]。Stream的核心操作包括中间操作(如`map`、`filter`)和终端操作(如`forEach`、`collect`)[^3]。 --- #### 二、经典练习题推荐(含代码示例) 1. **取元素绝对值** ```java Stream.of(1, -2, -3, 4, -5) .map(Math::abs) .forEach(System.out::println); // 输出1,2,3,4,5 [^2] ``` 2. **拆分单词并合并** ```java List<String> words = Arrays.asList("Hello World", "Java Stream"); words.stream() .flatMap(word -> Arrays.stream(word.split(" "))) .forEach(System.out::println); // 输出拆分后的单词[^1] ``` 3. **过滤奇数保留偶数** ```java List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10); list.stream() .filter(n -> n % 2 == 0) .forEach(System.out::println); // 输出2,4,6,8,10 ``` 4. **并行加速计算** ```java list.parallelStream() // 转换为并行 .filter(...); // 利用多核处理器提升性能[^1] ``` --- #### 三、学习资源推荐 1. **官方文档** Oracle Java 8 Stream官方教程:系统介绍Stream API的设计与使用场景。 2. **书籍《Java 8实战》** 详细讲解Stream、Lambda表达式等特性,含大量代码示例。 3. **综合教程与练习题集** - 的入门教程:涵盖Stream操作分类(中间操作、终端操作)和常见用例。 - 中的练习题:覆盖`map`、`filter`、`flatMap`和并行等核心操作。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值