java8新特性之强大的Stream API

特性:速度更快、代码更少,强大的Stream API、便于并行、最大化减少空指针异常 Optional。核心为Lambda表达式和Stream API,总共分为以下几个部分:

  1. Lambda表达式
  2. 函数式接口
  3. 方法引用与构造器引用
  4. Stream API
  5. 接口中的默认方法与静态方法
  6. 时间日期API
  7. 其他新特性

Stream API

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。

使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数 据库查询。

也可以使用Stream API来并行执行操作。

简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。

流(Stream) 到底是什么呢?

Stream流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列,以下三点注意。

  1. Stream 自己不会存储元素。
  2. Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
  3. Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。

Stream流操作的三大步

  1. 创建Stream流 //一个数据源 集合/数组/Stream静态方法 获取一个Stream流
  2. 中间操作 //对数据源的数据进行处理,过滤,筛选等等
  3. 终止操作 //执行中间操作链,产生结果

创建Stream流

1.集合创建Stream流

/****
 * java8中的Collection接口方法
 * stream() 顺序流
 * parallelStream()  并行流(后面再说这个)
 */
@Test
public void test01() {
    //声明一个list
    List<String> list = new ArrayList<String>();
    //  default Stream<E> stream() 顺序流
    //  default Stream<E> parallelStream()  并行流
    //  创建一个顺序流
    Stream stream1 = list.stream();
    //  创建一个并行流
    Stream stream2 = list.parallelStream();
}

2.数组创建Stream流

/****
 * 数组创建Stream流
 * Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
 */
@Test
public void test02() {
    //   Java8 中的 Arrays 的静态方法 stream() 可以获取数组流
    //    public static <T> Stream<T> stream(T[] array)
    String[] arr = new String[10];
    Stream stream = Arrays.stream(arr);
}

3.由值创建Stream流 Stream.of()

/****
 * 由值创建Stream流
 * Stream.of()
 */
@Test
public void test03() {
    //  可以使用静态方法 Stream.of(), 通过显式值创建一个流。它可以接收任意数量的参数。
    Stream stream = Stream.of("1", "2", "3", "4");
}

4.由函数创建Stream无限流 iterate.generate() Stream.generate()

/****
 * 由函数创建Stream无限流
 *
 * seed种子  f 一元运算接口
 * Stream.iterate(T seed,UnaryOperator f)
 *
 * supplier 供给型型接口
 * Stream.generate(Supplier supplier)
 */
@Test
public void test04() {
    // 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。
    //  args1 seed 开始种子, args2 一元函数式接口
    //迭代  public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
    Stream stream1 = Stream.iterate(0, (x) -> x + 1);

    //args 一个供给型接口
    // 生成   public static<T> Stream<T> generate(Supplier<T> s) :
    Stream stream2 = Stream.generate(() -> new Random().nextInt(100));

}

Stream流中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理,而在终止操作时一次性全部处理,称为“惰性求值”。看一下一些常用的api:

1.流中排除不符合条件的元素:filter

/****
    * filter(Predicate p)
    * 接收一个断言式接口,从流中排除不符合条件的元素
    */
   @Test
   public void test01() {
       Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
       //使用Stream流的静态方法 of创建一个Stream流
       Stream<Integer> stream = Stream.of(nums);
       //过滤操作 接收一个断言式接口 排除不符合条件的元素 输出结果
       stream.filter((x) -> x > 5).forEach(System.out::println);
   }

2.流中去除重复元素:distinct

/***
   * distinct()
   * 筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素
   */
  @Test
  public void test02() {
      Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
      //使用Stream流的静态方法 of创建一个Stream流
      Stream<Integer> stream = Stream.of(nums);
      //去除重复的元素
      stream.distinct().forEach(System.out::println);
  }

3.截取流获取前n个元素:limit

/***
 * limit(long maxSize)
 * 接收一个long型数值流中的元素个数不操过maxSize个,
 */
@Test
public void test03() {
    //使用Stream流的静态方法 generate创建一个Stream无限流
    Stream stream = Stream.generate(() -> new Random().nextInt(100));
    //截断流获取前n个元素 最大n个
    stream.limit(10).forEach(System.out::println);
}

4.跳过流前n个元素:skip

/***
 * skip(long n)
 * 接收一个long型数值 跳过流前n个元素获取后面的元素和 limit(n)相反
 */
@Test
public void test04() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用Stream流的静态方法 generate创建一个Stream无限流
    Stream stream = Arrays.stream(nums);
    stream.skip(3).forEach(System.out::println);
}

5.映射流中的每一个元素:map

/****
 * map(Function f)
 * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
 */
@Test
public void test05() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 generate创建一个Stream无限流
    Stream<Integer> stream = Arrays.stream(nums);
    // 将每个元素*2
    stream.map(x -> x * 2).forEach(System.out::println);

}

6.将流中元素映射成一个新的Double型元素:mapToDouble

/****
 * mapToDouble(ToDoubleFunction f)
 * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的Double型元素。
 * 注意原数组要可以转换成Double型
 */
@Test
public void test06() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 generate创建一个Stream无限流
    Stream<Integer> stream = Arrays.stream(nums);
    // 将每个元素 变成double
    stream.mapToDouble(x -> x * 2).forEach(System.out::println);

}

7.将流中元素映射成一个新的Long型元素:mapToDouble

/****
 * mapToLong(ToDoubleFunction f)
 * 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的Long型元素。
 * 注意原数组要可以转换成Long型
 */
@Test
public void test07() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 generate创建一个Stream无限流
    Stream<Integer> stream = Arrays.stream(nums);
    // 将每个元素 变成double
    stream.mapToDouble(x -> x * 2).forEach(System.out::println);

}

8.将流中的每个值都换成另一个流,然后把所有流连接成一个流:flatMap

/****
 * flatMap(ToDoubleFunction f)
 * 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
 */
@Test
public void test08() {
    String[] strings = {"hello lambda", "hello", "lambda"};
    //  Stream的静态方法 of获取流
    Stream<String> testStream = Stream.of(strings);
    //  流中的元素换成另一个流 分割处理 然后去重 打印
    testStream.flatMap(str -> Arrays.stream(str.split(" "))).distinct().forEach(System.out::println);
}

9.产生一个新流,其中按自然顺序排序:sorted

/**
 * sorted()
 * 产生一个新流,其中按自然顺序排序
 */
@Test
public void test09() {
    Integer[] nums = {1, 4, 2, 6, 3, 0, 9, 8, 7, 5};
    Arrays.stream(nums).sorted().forEach(System.out::println);
}

10.产生一个新流,其中按比较器顺序排序:sorted

/**
 * sorted(Comparator comparator)
 * 产生一个新流,其中按比较器顺序排序
 */
@Test
public void test10() {
    Integer[] nums = {-1, 4, -2, 6, -3, 0, 9, -8, 7, -5};
    //例如 按照绝对值排序
    Arrays.stream(nums).sorted(Comparator.comparing(x -> Math.abs(x))).forEach(System.out::println);
}

Stream流的终止操作

终端操作会从流的流水线生成结果。 其结果可以是任何不是流的值,例如:List、Integer、boolean

1.判断流中的元素是否全部满足某一个条件:allMatch

/****
 * allMatch(Predicate predicate)
 * 接收一个断言式接口 全匹配 返回boolean
 */
@Test
public void test01() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 of创建一个Stream流
    Stream<Integer> stream = Stream.of(nums);
    // 匹配数组元素是否全部大于等于0
    System.out.println(stream.allMatch((x) -> x >= 0));

}

2.判断流中的元素至少满足某一个条件:anyMatch

/****
 * anyMatch(Predicate predicate)
 * 接收一个断言式接口 至少匹配一个 返回boolean
 */
@Test
public void test02() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 of创建一个Stream流
    Stream<Integer> stream = Stream.of(nums);
    // 匹配数组元素是否有大于10的元素
    System.out.println(stream.anyMatch((x) -> x > 10));
}

3.判断流中的元素都不满足某一个条件:noneMatch

/****
 * noneMatch(Predicate predicate)
 * 接收一个断言式接口 是否没有匹配的元素 返回boolean
 */
@Test
public void test03() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
    //使用Stream流的静态方法 of创建一个Stream流
    Stream<Integer> stream = Stream.of(nums);
    // 匹配数组元素是否没有大于10的元素
    System.out.println(stream.noneMatch((x) -> x > 10));
}

4.返回流元素中的第一个元素:findFirst

/****
 * findFirst()
 * 返回Optional
 */
@Test
public void test04() {
    Integer[] nums = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用数组Arrays创建流
    Stream stream = Arrays.stream(nums);
    //返回流元素中的第一个元素
    Optional optional = stream.findFirst();
    System.out.println(optional.get());
}

5.返回流元素中的任意一个元素:findAny

/****
 * findAny()
 * 返回Optional
 */
@Test
public void test05() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用数组Arrays创建流
    Stream stream = Arrays.stream(nums);
    //返回流元素中的任意元素
    Optional optional = stream.findAny();
    System.out.println(optional.get());
}

6.返回流元素总数:count

/****
 * count()
 * 返回流元素总数 Long型
 */
@Test
public void test06() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用数组Arrays创建流
    Stream stream = Arrays.stream(nums);
    Long aLong = stream.count();
    System.out.println(aLong);
}

7.返回流元素中的最大元素:max

/****
 * max(Comparator comparator)
 * 返回流元素最大值
 * 接收一个比较器
 */
@Test
public void test07() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用数组Arrays创建流
    Stream<Integer> stream = Arrays.stream(nums);
    //返回流元素中的最大元素
    Optional optional = stream.max(Integer::compare);
    System.out.println(optional.get());

}

8.返回流元素中的最小元素:min

/****
 * max(Comparator comparator)
 * 返回流元素最小值
 * 接收一个比较器
 */
@Test
public void test08() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //使用数组Arrays创建流
    Stream<Integer> stream = Arrays.stream(nums);
    //返回流元素中的最小元素
    Optional optional = stream.min(Integer::compare);
    System.out.println(optional.get());

}

9.内部迭代:forEach

/****
 * forEach(Consumer consumer)
 * 接收一个消费性接口
 * 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
 * 相反,Stream API 使用内部 迭代——它帮你把迭代做了)
 */
@Test
public void test09() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    Arrays.stream(nums).forEach(System.out::println);
}

10.将流中元素反复结合起来,得到一个值:reduce

/***
 * reduce(BinaryOperator<T> accumulator)
 * 可以将流中元素反复结合起来,得到一个值。返回Optional
 * 接收一个二元运算接口
 */
@Test
public void test10() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //  依次累加
    Object object = Arrays.stream(nums).reduce((x, y) -> x + y).get();
    System.out.println(object);
}

11.将流中元素反复结合起来(有一个起始值),得到一个值:reduce

/***
 * reduce(T iden, BinaryOperator<T> accumulator)
 * 接收一个二元运算接口
 * 可以将流中元素反复结合起来,得到一个值。返回 T
 */
@Test
public void test11() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    //  iden 作为一个值先加入运算然后 依次累加
    Object object = Arrays.stream(nums).reduce(10, (x, y) -> x * y);
    System.out.println(object);
}

12.将流转换为List:stream.collect(Collectors.toList())

/***
 * collect(Collector collector)
 * 将流转换为其他形式。
 * 接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
 * Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例
 * Collectors.toList()
 * // 将数组变成了 list
 */
@Test
public void test12() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};

    Stream<Integer> stream = Arrays.stream(nums);
    List list = stream.collect(Collectors.toList());
    // 将数组变成了 list
    System.out.println(list.size());
}

13.将流转换为Set:stream.collect(Collectors.toSet())

/***
 * Collectors.toSet()
 * 将数组变成了 set
 */
@Test
public void test13() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    Stream<Integer> stream = Arrays.stream(nums);
    Set set = stream.collect(Collectors.toSet());
    // 将数组变成了 set
    System.out.println(set.size());
}

14.将流转换为Collection:stream.collect(Collectors.toSet())

/***
 * Collectors.toCollection(ArrayList::new)
 * Collectors.toCollection(HashSet::new)
 * 将数组变成了 Collection
 */
@Test
public void test14() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    Stream<Integer> stream = Arrays.stream(nums);
    Collection collection = stream.collect(Collectors.toCollection(HashSet::new));
    // 将数组变成了 collection
    System.out.println(collection.size());
}

15.计算流中元素的个数:stream.collect(Collectors.counting())

/***
 * Collectors.counting()
 * 计算流中元素的个数
 */
@Test
public void test15() {
    Integer[] nums = {2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 1};
    Stream<Integer> stream = Arrays.stream(nums);
    Long integer = stream.collect(Collectors.counting());
    System.out.println(integer);
}

16.计算流中元素的对象的某一个属性平均值:stream.collect(Collectors.summingDouble())

/***
 * Collectors.summingDouble(ToDoubleFunction mapper)
 * 计算流中元素的对象的某一个属性平均值
 * 接收一个 计算int、long、 double、值的函数 接口
 */
@Test
public void test16() {
    List<Employee> employees = new ArrayList<>(3);
    Employee employe1 = new Employee(1, "A1", 5000);
    Employee employe2 = new Employee(1, "A2", 8000);
    Employee employe3 = new Employee(1, "A3", 10000);
    employees.add(employe1);
    employees.add(employe2);
    employees.add(employe3);
    Stream<Employee> stream = employees.stream();
    Double sumSalary = stream.collect(Collectors.summingDouble(Employee::getSalary));
    System.out.println(sumSalary);
}

17.计算流中元素的对象的某一个属性平均值:stream.collect(Collectors.averagingDouble())

/***
 * Collectors.averagingDouble(ToDoubleFunction mapper)
 * 计算流中元素的对象的某一个属性平均值
 * 接收一个 计算int、long、 double、值的函数 接口
 */
@Test
public void test17() {
    List<Employee> employees = new ArrayList<>(3);
    Employee employe1 = new Employee(1, "A1", 5000);
    Employee employe2 = new Employee(1, "A2", 8000);
    Employee employe3 = new Employee(1, "A3", 10000);
    employees.add(employe1);
    employees.add(employe2);
    employees.add(employe3);
    Stream<Employee> stream = employees.stream();
    Double avgSalary = stream.collect(Collectors.averagingDouble(Employee::getSalary));
    System.out.println(avgSalary);
}

18.连接流中元素的对象的某一个属性字符串:stream.collect(Collectors.joining())

/***
 * Collectors.joining()
 * 连接流中元素的对象的某一个属性字符串
 */
@Test
public void test18() {
    List<Employee> employees = new ArrayList<>(3);
    Employee employe1 = new Employee(1, "A1", 5000);
    Employee employe2 = new Employee(1, "A2", 8000);
    Employee employe3 = new Employee(1, "A3", 10000);
    employees.add(employe1);
    employees.add(employe2);
    employees.add(employe3);
    Stream<Employee> stream = employees.stream();
    String names = stream.map((x) -> x.getName() + "-").collect(Collectors.joining());
    System.out.println(names);
}

19.收集器中最小值:stream.collect(Collectors.minBy())

/***
 * Collectors.maxBy(Comparator comparator)
 * 根据比较器选择最小值
 * 接收一个比较器
 */
@Test
public void test19() {
    Integer[] nums = {1, 2, 3, 4, 5, 7, 8, 9, 3, 4, 56};
    Stream<Integer> stream = Arrays.stream(nums);
    Optional optional = stream.collect(Collectors.minBy(Integer::compareTo));
    System.out.println(optional.get());
}

20.收集器中某一属性之和:stream.collect(Collectors.reducing())

    /***
     * Collectors.reducing(U identity,Function<? super T, ? extends U> mapper,BinaryOperator<U> op)
     * 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
     * 接收二元计算接口
     */
    @Test
    public void test20() {
        List<Employee> employees = new ArrayList<>(3);
        Employee employe1 = new Employee(1, "A1", 5000);
        Employee employe2 = new Employee(1, "A2", 8000);
        Employee employe3 = new Employee(1, "A3", 10000);
        employees.add(employe1);
        employees.add(employe2);
        employees.add(employe3);

        Stream<Employee> stream = employees.stream();
//        identity : 0d 类型和后面的元素类型 保持一致
        Double aDouble = stream.collect(Collectors.reducing(0d, Employee::getSalary, Double::sum));
        System.out.println(aDouble);
    }

21.收集器转换为另一类型然后处理:stream.collect(Collectors.collectingAndThen())

    /***
     *  Collectors.collectingAndThen(Collector<T,A,R> downstream,Function<R,RR> finisher)
     *  转换函数返回的类型
     *  包裹另一个收集器,对其结果转换函数
     */
    @Test
    public void test21() {
        List<Employee> employees = new ArrayList<>(3);
        Employee employe1 = new Employee(1, "A1", 5000);
        Employee employe2 = new Employee(1, "A2", 8000);
        Employee employe3 = new Employee(1, "A3", 10000);
        employees.add(employe1);
        employees.add(employe2);
        employees.add(employe3);
        Stream<Employee> stream = employees.stream();
//        转换函数返回的类型 返回set
//        包裹另一个收集器,对其结果 set 进行处理 判断时候为空
        Boolean bool = stream.collect(Collectors.collectingAndThen(Collectors.toSet(), Set::isEmpty));
        System.out.println(bool);
    }

22.收集器按照某一属性分组:stream.collect(Collectors.groupingBy())

/***
 *  根据某属性值对流分组,属 性为K,结果为V
 *  Collectors.groupingBy(Function<? super T, ? extends K> classifier)
 *  传入一个 代表流元素的属性
 *  返回 以属性为key value为 list的map
 */
@Test
public void test22() {
    List<Employee> employees = new ArrayList<>(3);
    Employee employe1 = new Employee(1, "A1", 5000);
    Employee employe2 = new Employee(1, "A2", 8000);
    Employee employe3 = new Employee(1, "A3", 10000);
    employees.add(employe1);
    employees.add(employe2);
    employees.add(employe3);
    Stream<Employee> stream = employees.stream();
    Map map = stream.collect(Collectors.groupingBy(Employee::getName));
    Set set=map.entrySet();
    for (Object str:set) {
        System.out.println(str);
        System.out.println(map.get(str));
    }

}

23.收集器按照某一属性(boolean类型)分组:stream.collect(Collectors.partitioningBy())

/***
 *  根据true或false进行分区
 *  Collectors.groupingBy(Function<? super T, ? extends K> classifier)
 *  传入一个 代表流元素的属性
 *  返回 以属性(flase/true)为key value为 list的map
 */
@Test
public void test23() {
    List<Employee> employees = new ArrayList<>(3);
    Employee employe1 = new Employee(1, "A1", 5000,false);
    Employee employe2 = new Employee(1, "A2", 8000,true);
    Employee employe3 = new Employee(1, "A3", 10000,false);
    employees.add(employe1);
    employees.add(employe2);
    employees.add(employe3);
    Stream<Employee> stream = employees.stream();
    Map map = stream.collect(Collectors.partitioningBy(Employee::getFlag));
    Set set=map.entrySet();
    for (Object str:set) {
        System.out.println(str);
        System.out.println(map.get(str));
    }

}

辅助类Employee

/**
 * @author black猫
 * @date 2019-11-25
 * 员工类
 */
public class Employee {

    private int id;
    private String name;
    private double salary;
    private boolean flag;

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public boolean getFlag() {
        return flag;
    }

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    public  Employee(int id, String name, double salary){
        this.id=id;
        this.name=name;
        this.salary=salary;
    }

    public  Employee(int id, String name, double salary,boolean flag){
        this.id=id;
        this.name=name;
        this.salary=salary;
        this.flag=flag;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '/'' +
                ", salary=" + salary +
                ", flag=" + flag +
                '}';
    }
}

我的小结

Stream是java8提供的java.util.stream.*包下的强大Api,结合lambda表达式,可以快速、高效、便捷的对集合数据进行处理, 熟练掌握之后,就像写sql一样操作集合数据,功能强大,代码不再那么臃肿、冗余。爽!!!

原文 

https://blackcat.top/2019/11/25/A006/

本站部分文章源于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对文章转载有任何疑问请告之我们,以便我们及时纠正。

PS:推荐一个微信公众号: askHarries 或者qq群:474807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习资源,目前受益良多

转载请注明原文出处:Harries Blog™ » java8新特性之强大的Stream API

赞 (0)
分享到:更多 ()

评论 0

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址