当前位置:   article > 正文

java8 Stream API

java8 stream api

一. Stream API 概述

jva8在java.util.stream包下,通过Stream真正的引入函数式编程,可以对数据进行复杂的查找,过滤映射,提供了高效且易于使用的处理数据的方式,例如想要获取集合中的某些特定元素,在以前需要遍历集合,对每个元素进行筛选代码比较多,通过Stream,一行代码就可以搞定

  1. Stream 不会存储元素
  2. Stream 不会改变源对象,会返回一个持有结果的心的Stream
  3. Stream 是延迟执行的(创建一个有数据的Stream元数据流–>通过Stream对数据进行处理–>终止操作产生结果,只有调用终止操作以后前面的计算才会执行,否则算调用计算的方法,不执行的,终止后的Stream不可以再次继续进行中间处理,若想处理重新获取流,或一次性执行中间处理完毕后再调用终止)

二. Stream实例化方式

  1. 通过集合调用stream()方法获取Stream顺序流
  2. 通过集合调用parallelStream()方法获取Stream并行流
  3. 通过数组获取Stream
  4. 无限流

创建Stream流的方式代码示例

 	@Test
    public void test1(){
        //创建Stream的方式

        List<Persion> persionList = new ArrayList<>();

        persionList.add(new Persion("小明",22,2000.00));
        persionList.add(new Persion("小红",34,2500.00));
        persionList.add(new Persion("AA",18,1800.00));
        persionList.add(new Persion("小强",40,3500.00));
        persionList.add(new Persion("小黑",22,2200.00));
        persionList.add(new Persion("小花",27,2000.00));
        persionList.add(new Persion("bb",16,1500.00));

        //1.通过集合中的.stream()获取Stream流,注意该方法返回的是一个顺序流(根据向集合中添加的顺序)
        Stream<Persion> stream1 = persionList.stream();

        //通过集合中的parallelStream()获取一个并行流,有点像通过几个线程同时去取,顺序可能发生改变
        Stream<Persion> stream2 = persionList.parallelStream();

        //2.通过Arrays工具类中的stream(T[] array)静态方法获取Stream流
		//集合转数组
        Persion[] perArray =  persionList.toArray(new Persion[persionList.size()]);

        Stream<Persion> stream = Arrays.stream(perArray);
        //注意点,在使用Arrays中的stream()方法获取Stream流时,根据像方法中传递的
        //泛型T不同,其返回的Stream不同
        int[] iArray = new int[]{1,4,9};
        long[] lArray = new long[]{11l,22l,6l};
        double[] dArray = new double[]{7.8,14.3,5.1};
        IntStream intStream = Arrays.stream(iArray);
        LongStream longStream = Arrays.stream(lArray);
        DoubleStream doubleStream = Arrays.stream(dArray);

        //3.通过Stream的of()方法创建,将数据传入of()方法中
        Stream<Integer> stream3 = Stream.of(1, 2, 3, 4);

        //4.创建无限Stream流,输出0-10的偶数,
        //iterate()中0代表开始位置,后面的Lambda传递的是Function<T,T> +2返回
        //limit为中止操作,10
        //forEach()为执行体
        Stream.iterate(0,t -> t +2).limit(10).forEach(System.out :: println);

        //生成,获取10个随机数
        Stream.generate(Math :: random).limit(10).forEach(System.out :: println);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

三. Stream 中间操作(通过Stream操作数据)

概述

使用Stream对数据继续操作,例如过滤筛选,映射,排序等,注意点: Stream被称为"惰性求值",在进行中间操作时,如果最终不调用终止,则中间操作无效,在调用终止操作后,若有后续的操作需要重新获取Stream流,否则无效

1. 筛选切片

1)方法概述
在这里插入图片描述
2)代码示例

	@Test
    public void test1(){
		//persionList是一个List<Persion>集合
        //获取Stream流
        Stream<Persion> stream = persionList.stream();
        //筛选与切片
        //1.filter(Predicate p) 过滤,接收 Lambda ,从流中排除某些元素
        //获取集合中age大于20的数据
        stream.filter(p -> p.getAge()>20).forEach(System.out :: println);
       
        //2.limit(n) 截断流,使其元素不超过给定的数量n
        Stream<Persion> stream2 = persionList.stream();
        //获取集合中前三条数据
        stream2.limit(3).forEach(System.out :: println);
        
        //3.skip(n) 跳过元素,返回一个不包括n以前个数的元素,若流中不足n个数据则返回就返回一个空流
        //去掉集合中前3个元素
        persionList.stream().skip(3).forEach(System.out :: println);
        
        //4.distinct() 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
        //去重
        persionList.stream().distinct().forEach(System.out :: println);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

2. 映射

1)方法概述
在这里插入图片描述
2)代码示例

	@Test
    public void test2(){

        //1.map(Function f) 接收一个函数作为参数,将元素映射成其它形式或获取信息,
        //该函数会被应用到每个元素上,并将其映射到新的元素
        //map()会依次对Stream中的每个元素执行
        //示例1,将集合中所有字符串转换为大写
        List<String> stringList = Arrays.asList("aa", "bb", "cc");
        stringList.stream().map(str -> str.toUpperCase()).forEach(System.out :: println);

        //示例2,获取集合姓名包含"小"的员工的姓名
        Stream<String> ageStream = persionList.stream().map(Persion::getName);
        ageStream.filter(name -> name.contains("小")).forEach(System.out :: println);

        //2.flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流
        //然后把所有流连接成一个流,与map()方法的区别有点像集合中的add(),与addAll()
        //假设调用add方法向集合中添加另外一个集合是会变成[obj1, obj2, obj3,List[obj1,obj2,obj3]]
        //如果调用addAll(),则会重新组合为一个集合,而不是集合作为集合类型元素存在
        //假设通过map()获取的数据是Stream<Stream<T>>,此时如果想要继续过滤就有点麻烦,可以通过flatMap()
        //例如调用下面自定义的fromStringToStream(String str)方法,返回多个Stream流,

        Stream<Stream<Character>> streamStream = stringList.stream().map(ArrayTest::fromStringToStream);
        //输出stringList中的所有char字符,由于调用ArrayTest类中中的fromStringToStream()方法
        //返回的是Stream<Stream<Charcter>>,需要两层farEach()
        streamStream.forEach(s -> {
            s.forEach(System.out :: println);
        });
        //使用flamtMap,则会将多个Stream流重新组合为一个
        Stream<Character> characterStream = stringList.stream().flatMap(ArrayTest::fromStringToStream);
        characterStream.forEach(System.out :: println);
    }

    //将String字符串,转换为char集合,并将char集合转换为Stream流返回
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList<>();
        for(Character c : str.toCharArray()){
            list.add(c);
        }
        return list.stream();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  1. map() 与 filatMap() 使用示例2
	//1.使用map转换为其它类型
	List<ResponseDataDTO> responseDataDTOS = result.stream().map(a -> {
                    ResponseDataDTO responseDataDTO = new ResponseDataDTO();
                    responseDataDTO.setC00("");
                    responseDataDTO.setC01(a.getRmtype());
                    responseDataDTO.setC02(a.getRate1().toString());
                    responseDataDTO.setC03("");
                    responseDataDTO.setC04("");
                    responseDataDTO.setC05("");
                    responseDataDTO.setC06(a.getRatecode());
                    responseDataDTO.setC07("");
                    responseDataDTO.setC08(a.getRate1().toString());
                    responseDataDTO.setC09(a.getPackages());
                    return responseDataDTO;
                }).collect(Collectors.toList());
	 
	 //2.使用map+flatMap多list合并
	 List<String> accntList = searchcheckin.getData().getList()
	         .stream()
	         .filter(o -> !CollectionUtils.isEmpty(o.getList()))
	         .map(OrderRespDTO::getList)
	         .flatMap(Collection::stream)
	         .filter(occ -> !CollectionUtils.isEmpty(occ.getGuests()))
	         .map(OccupationDTO::getGuests)
	         .flatMap(Collection::stream)
	         .filter(g -> StringUtils.isNotBlank(g.getAccNt()))
	         .map(GuestDTO::getAccNt)
	         .distinct()
	         .collect(Collectors.toList());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29

3. 排序

1)方法概述
在这里插入图片描述

2)代码示例

	@Test
    public void test3(){
        //排序
        //1.sorted() 自然排序
        List<Integer> integers = Arrays.asList(12, 34, 20, 70, 60);
        integers.stream().sorted().forEach(System.out::println);

        //如果想要自定义类对象实现自然排序,该类需要实现Comparator接口,制定排序规则否则报错
        //否则使用sorted定制排序
        //2.sorted(Comparator com) 定制排序(如果年龄相等则使用工资排序)
        persionList.stream().sorted((p1, p2) ->{
            int ageVal = Integer.compare(p1.getAge(),p2.getAge());
            if(ageVal != 0){
                return ageVal;
            }else {
                return Double.compare(p1.getSalary(), p2.getSalary());
            }
        }).forEach(System.out :: println);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

四. Stream 的终止操作

终止操作,生成流的执行结果

1. 查找与匹配

1)方法概述
在这里插入图片描述
在这里插入图片描述
2) 代码示例

	@Test
	public void test4() {

        List<Persion> persionList = new ArrayList<>();
        persionList.add(new Persion("小明", 22, 2000.00));
        //add()......

        //一下的操作中都可以在加入中间操作实现过滤后再进行终止

        //1.allMatch(Predicate p) 检查元素是否匹配,所有都是true,才是true
        //判断是否集合中的所有年龄都大于18岁
        Boolean b1 = persionList.stream().allMatch(p -> p.getAge() > 18);

        //2.anyMatch(Predicate p) 检查是否有一个元素匹配,有一个为true则为true
        //判断集合中是否有工资大于5000
        Boolean b2 = persionList.stream().anyMatch(p -> p.getSalary() > 5000);

        //3.noneMatch(Predicate p) 检查是否没有匹配的
        //判断集合中是否有叫小明的,如果有则返回false
        boolean b3 = persionList.stream().noneMatch(p -> p.getName().contains("小明"));

        //4.findFirst 返回第一个元素,Option<>
        Optional<Persion> persionOptional = persionList.stream().findFirst();

        //5.findAny 返回当前流中任意元素
        Optional<Persion> anyPersion = persionList.stream().findFirst();

        //6.count 返回当前流中元素个数
        //获取工资超过2000的个数
        long count = persionList.stream().filter(p -> p.getSalary()>2000.00).count();

        //7.max(Comparator c) 返回当前流中最大的元素
        //返回最大工资的
        Optional<Persion> max = persionList.stream().max((p1, p2) -> Double.compare(p1.getSalary(), p2.getSalary()));

        //min(Comparator c) 返回最小的元素
        //返回年龄最小的
        Optional<Persion> min = persionList.stream().min((p1, p2) -> Integer.compare(p1.getAge(), p2.getAge()));
        //返回最小年龄
        persionList.stream().map(p -> p.getAge()).min(Integer :: compare);

        //8.forEach(Consumer c) 内部迭代
        persionList.stream().forEach(System.out :: println);
    }
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

2. 规约

1)方法概述
在这里插入图片描述
2)代码示例

	@Test
    public void test5(){
        //1.reduce(T identity, BinaryOperator) 将六种的元素结合起来得到一个值并返回
        //查看BinaryOperator源码进行分析
        //BinaryOperator继承BiFunction<T,T,T>接口,该接口传递两个T类型参数,返回一个T类型参数
        //刚好符合Integer与Dubbo中的sum方法
        //计算1-10的自然数的和
        List<Integer> list = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
        Integer reduce = list.stream().reduce(0, Integer::sum);
        //普通方式
        Integer reduce2 = list.stream().reduce(0, (i1, i2) -> i1 + i2);

        //2.reduce(BinaryOperator)
        //获取集合中工资总和
        List<Persion> persionList = new ArrayList<>();
        persionList.add(new Persion("小明", 22, 2000.00));
        //add()...

        Optional<Double> reduce1 = persionList.stream().map(Persion::getSalary).reduce(Double::sum);
        //普通方式
        persionList.stream().map(Persion::getSalary).reduce((d1, d2) -> d1+d2);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

3. 收集

1)方法概述
在这里插入图片描述
StreamAPI 收集 Collectors,将过滤终止的Stream结果数据收集为其他类型,与Stream收集终止操作配合使用
在这里插入图片描述
在这里插入图片描述

2)代码示例

	@Test
    public void test6(){
        //1.collection(Collector c); 配合Collector中的静态方法,将Stream过滤终止后的数据
        //收集为其他类型,例如List, set等

        List<Persion> persionList = new ArrayList<>();

        persionList.add(new Persion("小明", 22, 2000.00));
       	//add()......

        //查找工资大于2000的员工返回List,或set
        List<Persion> pList = persionList.stream().filter(p -> p.getSalary()>2000).collect(Collectors.toList());
        pList.forEach(System.out :: println);
        //返回set集合
        Set<Persion> collect = persionList.stream().filter(p -> p.getSalary() > 2000).collect(Collectors.toSet());
        //返回Collection,根据toCollection中的类型来决定返回值的类型
        ArrayList<Persion> collect1 = persionList.stream().filter(p -> p.getSalary() > 2000)
        			.collect(Collectors.toCollection(ArrayList::new));
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

4. 分组

Map<String, List<NewRoomStatusDataRespDTO>> map = respList.stream().collect(Collectors.groupingBy(NewRoomStatusDataRespDTO::getBldCd));
  • 1

5. 判断获取集合中符合条件元素的示例

//返回集合中userName等于"aaa"的数据,如果不存在返回null;
User user = userList.stream().filter(u -> u.getUserName().equals("aaa")).findAny().orElse(null);
//获取集合中对象元素指定属性
List<String> orderNoList=list.stream().map(Order::getOrderNo).collect(Collectors.toList());
  • 1
  • 2
  • 3
  • 4

6. toMap 示例

	private List<RoomStatusAllDataRespDTO> generateRoomsAllStateResult(List<DayRoomsStateDTO> roomStateList) {
        //1.value为原数据,重复key覆盖
        Map<String, DayRoomsStateDTO> map1 = roomStateList.stream().collect(Collectors.toMap(DayRoomsStateDTO::getDate, Function.identity(), (oldKey, newKey) -> newKey));
        //2.value为通过Stream数据封装出的类型(Map中的value为 RoomStateDTO是DayRoomsStateDTO::getRoom返回的数据类型),重复key覆盖
        Map<String, RoomStateDTO> map2 = roomStateList.stream().collect(Collectors.toMap(DayRoomsStateDTO::getDate, DayRoomsStateDTO::getRoom, (oldKey, newKey) -> newKey));
        //3.value为通过Stream数据封装出的类型,重复key放入list中
        Map<String, List<RoomStateDTO>> map = roomStateList.stream().collect(Collectors.toMap(DayRoomsStateDTO::getDate,
                dayRoomsStateDTO -> {
                            ArrayList<RoomStateDTO> list = new ArrayList<>();
                            list.add(dayRoomsStateDTO.getRoom());
                            return list;
                        },
                        (oldList, newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        }));
        //4.转map并排序
        TreeMap<String, List<Working>> collect =
                workings.stream().collect(Collectors.toMap(Working::getInvoicePage,
                        e -> {
                            ArrayList<Working> list = new ArrayList<>();
                            list.add(e);
                            return list;
                        },
                        (oldList, newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        },
                        () -> new TreeMap<>(Comparator.comparing(Integer::valueOf))));
        return null;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

7. 分组去重示例

	private List<OccupationDTO> getOccupationDTO(PmsOrderBean order, List<PmsOccupationGuest> occGuestList) {
        if (CollectionUtils.isEmpty(occGuestList)) {
            return null;
        }
        
        //1.由于三方返回订单信息以入住人为维度,先通过房间号进行分组
        //(groupingBy会把相同key的元素合并成一个List作为Map的value)
        Map<String, List<PmsOccupationGuest>> guestMap = occGuestList.stream()
        .collect(Collectors.groupingBy(PmsOccupationGuest::getRoomNo));
        
        //2.转换为对应占房类型,并根据房间号去重
        List<OccupationDTO> occupateionList = occGuestList.stream()
        	.map(occ -> {
            	return occ.convertOccupationDTO(order.getOrderState());
        	})
        	.collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<OccupationDTO>(Comparator.comparing(OccupationDTO::getRoomNo))), ArrayList::new));
        	
        //3.拿到占房结构信息后遍历,通过房间号获取分组中对应信息,填充入住人信息
        occupateionList.forEach(occ -> {
            List<GuestDTO> gList = guestMap.get(occ.getRoomNo()).stream()
            	.map(PmsOccupationGuest::convertGuestDTO)
            	.collect(Collectors.toList());
            
            occ.setGuests(gList);
        });
        return occupateionList;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

8. 分组重点优化

//1.在使用groupingBy分组时(groupingBy会把相同key的元素合并成一个List作为Map的value)
 Map<String, List<PmsOccupationGuest>> guestMap = occGuestList.stream()
        .collect(Collectors.groupingBy(PmsOccupationGuest::getRoomNo));
  • 1
  • 2
  • 3
//可以使用上方的groupingBy进行优化(因为重复key时也是接近添加到list中作为map的value)
Map<String, List<GuestDTO>> guestMap = pmsOrderList.stream()
                .map(this::convertGuestDTO)
                .map(Map::entrySet)
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(Map.Entry::getKey,
                        entry -> {
                            ArrayList<GuestDTO> list = new ArrayList<>();
                            list.add(entry.getValue());
                            return list;
                        }, (oldList, newList) -> {
                            oldList.addAll(newList);
                            return oldList;
                        }));

	//也可以将该方法修改为返回一个GuestDTO对象,而不是Map
	private Map<String, GuestDTO> convertGuestDTO(GetArrivingResp.OrderListBean orderPms) {
        Map<String, GuestDTO> guestMap = new HashMap<>();
        GuestDTO guest = new GuestDTO();
        guest.setGuestName(orderPms.getName());
        guest.setGuestMobile(orderPms.getMobilePhone());
        guest.setGuestCardNum(orderPms.getIdentity());
        guest.setCheckInTime(DateUtil.stringToLongSecond(orderPms.getBeginTime()));
        guest.setCheckOutTime(DateUtil.stringToLongSecond(orderPms.getEndTime()));
        //0 预登记  1预定  2 入住  3离店  10取消
        guest.setGuestStatus(this.orderStatus(orderPms.getAbleCheckIn()));
        //guest.setAccNt(orderPms.getAccountNo());
        guestMap.put(orderPms.getUnionAccountNo(), guest);
        return guestMap;
    }

//然后使用: 表示先按照getUnionAccountNo进行分组,
//分组后,通过 Collectors.mapping()对每个分组中的数据进行类型转换拿到GuestDTO
//最终拿到 Map<String, List<GuestDTO>>
 Map<String, List<GuestDTO>> gMap = pmsOrderList.stream()
                .collect(Collectors.groupingBy(GetArrivingResp.OrderListBean::getUnionAccountNo,
                        Collectors.mapping(orderBean -> this.convertGuestDTO(orderBean), Collectors.toList())));

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

  • 1

9. Match

// 只要集合中有一个匹配,就返回 true
 boolean anyMatch(Predicate<? super T> predicate);
 // 集合中所有元素都匹配,才返回 true
 boolean allMatch(Predicate<? super T> predicate);
 // 集合中所有元素都不匹配,返回 true
 boolean noneMatch(Predicate<? super T> predicate);


 public class Main {public static void main(String[] args) {
         List<String> list = MyUtil.getList();
         System.out.println("集合中的所有元素是否都以 a 开头");
         System.out.println(list.stream().allMatch(s -> s.startsWith("a")));System.out.println("集合中是否存在元素以 a 开头");
         System.out.println(list.stream().anyMatch(s -> s.startsWith("a")));System.out.println("集合中的元素是否都不以 a 开头(相当于 allMatch 的取反):");
         System.out.println(list.stream().noneMatch(s -> s.startsWith("a")));
     }
 }
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/612010
推荐阅读
相关标签
  

闽ICP备14008679号