集和进阶(二)

Collection的其他相关知识

可变参数

  • 就是一种特殊形参,定义在方法、构造器的形参列表里,格式是:数据类型…参数名称。

  • 特点:可以不传数据给它;可以传一个或者同时传多个数据给它;也可以传一个数组给它。

  • 好处:常常用来灵活的接收数据。

  • 注意事项

    • 可变参数在方法内部就是一个 数组
    • 一个形参列表中,只能有一个可变参数。
    • 可变参数必须放在形参列表的最后面。

Collections类

  • 是一个用来操作集和的工具类

常用静态方法

实战:斗地主发牌

  • 需求分析:
    • 业务:总共有54张牌。点数分别要组合4种花色,大小王各一张。
    • 点数:3、4、5、6、7、8、9、10、J、Q、K、A、2
    • 花色:♦、♣、♥、♠
    • 大小王: 小王👿 大王😈
    • 斗地主:发出51张牌,剩下3张作为底牌。

Map集合

概述

  • Collection:单列集和,每个元素包含一个值。
  • Map:双列集和,每个元素包含一个键值对。
    • Map集合称为双列集合,格式:{key1=value1,key2=value2,key3=value3,…},一次需要存一对数据做为一个元素。
    • Map集合的每个元素“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”。
    • Map集合的所有键是不允许重复的,但值可以重复,键和值是一一对应的,每一个键只能找到自己对应的值。
  • 应用场景:淘宝购物车
    • {商品1 = 华为手机, 商品2 = 华为手机, 商品3 = 苹果手机}
    • 需要存储一一对应的数据时,就可以考虑使用Map集合来做。

Map集合体系

Map集合特点

  • 注意:Map系列集合的特点都是由键决定的,值只是一个附属品,值是不做要求的。
  • 细分
    • HashMap(由键决定特点):无序、不重复、无索引。(用的最多)
    • LinkedHashMap(由键决定特点):有序、不重复、无索引。
    • TreeMap(由键决定特点):按照大小默认升序排序、不重复、无索引。
  • 创建一个HashMap
1
Map<String, Integer> map = new HashMap;  //一行经典代码
  • 相同的键,后存入的值会覆盖之前的值。

Map集合常用方法

Map集合遍历方式

  1. 键找值

    先获取Map集合全部的键,再通过遍历键来找值。

    键找值用到的方法

  2. 键值对

    把“键值对”看成一个整体进行遍历(难度较大)。

    键值对用到的方法

    1
    Set<Map.Entry<KeyType, ValueType>> entries = map.entryset();

    该方法会返回一个Set集合,元素类型为将键值对封装成的Entry对象,之后遍历该Set集合即可。

  3. Lambda

    JDK1.8开始之后的新技术(非常的简单)。

    Lambda用到的方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //Lambda表达式
    map forEach(k, v) -> {
    System.out.println(k + "-->" + v)
    }

    //完整表达式:BiConsumer接口匿名内部类 重写其中的accept方法
    map forEach(new BiConsumer<KeyType, ValueType>){
    @Override
    public void accept(KeyType k, ValueType v){
    System.out.println(k + "-->" + v)
    } //accept方法实际上也是通过第二种遍历方法 即把键值对封装成Entry对象来实现遍历的
    }

HashMap

  • 特点:无序、不重复、无索引。(用的最多)

  • 底层原理:

    • HashMap跟HashSet的底层原理是一模一样的,都是基于哈希表实现的。

    • 实际上原来学的Set系列集合的底层就是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据。

      1
      2
      3
      public HashSet(){
      map = new HashMap<>();
      }
  • HashMap是基于哈希表实现的(以键为基准):

    • HashMap集合是一种增删改查数据,性能都较好的集合。
    • 但是它是无序,不能重复,没有索引支持的(由键决定特点)。
    • HashMap的键依赖hashCode方法和equals方法保证键的唯一。
    • 如果键存储的是自定义类型的对象,可以通过重写hashCode和equals方法,这样可以保证多个对象内容一样时,HashMap集合就能认为是重复的。

LinkedHashMap

  • 特点:有序、不重复、无索引。
  • 实际上原来学习的LinkedHashset集合的底层原理就是LinkedHashMap,只是Set集合中的元素只要键数据,不要值数据。
  • LinkedHashMap是基于哈希表实现的(以键为基准),只是每个键值对元素又额外的多了一个双链表的机制记录元素顺序(保证有序)。

TreeMap

  • 特点:不重复、无索引、可排序。(按照键的大小默认升序排序,只能对键排序)
  • TreeMap跟TreeSet集合的底层原理是一样的,都是基于红黑树实现的排序。
  • TreeMap集合同样也支持两种方式来指定排序规则:
    • 让类实现Comparable接口,重写比较规则。
    • TreeMap集合有一个有参数构造器,支持创建Comparator比较器对象,以便用来指定比较规则。(匿名内部类->Lambda)

补充知识:集合的嵌套

  • Map集合案例-省和市

    • 需求:要求在程序中记住如下省份和其对应的城市信息,记录成功后,要求可以查询出湖北省的城市信息。

      江苏省 = 南京市,扬州市,苏州市,无锡市,常州市

      湖北省 = 武汉市,孝感市,十堰市,宜昌市,鄂州市

      河北省 = 石家庄市,唐山市,邢台市,保定市,张家口市

    • 分析:定义一个Map集合,键用表示省份名称,值表示城市名称,注意:城市会有多个。

    • 根据“湖北省”这个键获取对应的值展示即可。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //创建一个map集合 用于存放各省信息 键-省(String) ---> 值-市的集合(List<String>)
    Map<String, List<String>> map = new HashMap<>();

    //创建一个ArrayList 用于表示各省 存放市的信息
    List<String> cityName1 = new ArrayList<>();
    Collentions.addAll(cityName1, "xx市", "xx市", ……, "xx市");
    map.put("xx省", cityName1);
    //……以此类推

    //按照省名读取信息
    List<String> cities = map.get("xx省");
    //……其他操作

Stream流

Stream概述

  • 也叫Stream流,是Jdk8开始新增的一套API(java.util.stream.*),可以用于操作集合或者数组的数据
  • 优势:Stream流大量的结合了Lambda的语法风格来编程,提供了一种更加强大,更加简单的方式操
    作集合或者数组中的数据,代码更简洁,可读性更好。

  • 案例

    • 需求:把集合中所有以“张”开头,且是3个字的元素存储到一个新的集合。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      List<String> names = new ArrayList<>();
      list.add("张无忌");
      list.add("周芷若");
      list.add("赵敏");
      list.add("张强");
      list.add("张三丰");

      //Stream流
      List<String> newList = names.stream()
      .filters(s -> s.startsWith("张") && s.length()==3)
      .collect(Collectors.toList());
      //或者把条件拆开
      List<String> newList = names.stream()
      .filters(s -> s.startsWith("张")).(a -> a.length()==3)
      .collect(Collectors.toList());

Stream流的使用步骤

Stream流使用步骤

Stream代表一条流水线,对数据源进行加工处理后获取结果。

  1. 获取Stream流;
  2. 调用Stream流各种方法对数据进行处理、运算;
  3. 获取处理结果,遍历、统计、收集到一个新集合中返回。

Stream的常用方法

  1. 获取Stream流

    • 获取集合的Stream流

  2. Stream流常见的中间方法

    • 中间方法指的是调用完成后会返回新的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
    41
    42
    43
    44
    45
    46
    47
    48
    49
    List<Double> scores = new ArrayList<>();
    Collections.addAll(scores, 88.5, 100.0, 60.0, 99.0, 9.5, 99.6, 25.0);

    // 需求1:找出成绩大于等于60分的数据,并升序后,再输出。
    scores.stream().filter(s -> s >= 60).sorted().forEach(s -> System.out.println(s)):

    /*-------------------------------------------------------------------------*/

    List<Student> students =new ArrayList<>();
    Student s1 =new Student(name:"蜘蛛精", age: 26, height:172.5);
    Student s2 =new Student(name:"蜘蛛精", age:26, height:172.5);
    Student s3 = new Student(name:"紫霞", age:23, height:167.6);
    Student s4 = new Student(name:"白晶晶", age:25, height:169.0);
    Student s5 = new Student(name:"牛魔王", age: 35, height:183.3);
    Student s6 = new Student(name:"牛夫人", age:34, height:168.5);
    Collections.addAll(students, s1, s2, s3, s4, s5, s6);

    // 需求2:找出年龄大于等于23,且年龄小于等于30岁的学生,并按照年龄降序输出.
    students.stream().filter(s -> s.getAge() >= 23 && s.getAge() <= 30)
    .sorted((o1, o2) -> o2.getAge() - o1.getAge()) //Lambda
    .forEach(System.out::println); //静态方法引用 遍历后打印

    // 需求3:取出身高最高的前3名学生,并输出。
    students.stream().sorted((o1,o2)-> Double.compare(o2.getHeight(), o1.getHeight()))
    .limit(3) //取前三个
    .forEach(System.out::println);

    // 需求4:取出身高倒数的2名学生,并输出。
    students.stream().sorted((o1,02)-> Double.compare(o2.getHeight(),o1.getHeight()))
    .skip(students.size()-2) //只取最后两个(跳过前边四个)
    .forEach(System.out::println);

    // 需求5a:找出身高超过168的学生叫什么名字,要求去除重复的名字,再输出。
    students.stream().filter(s ->s.getHeight() > 168)
    .map(s -> s.getName()) //映射(把对象加工成名字).map(Student::getName())使用特殊类型方法引用
    .distinct() //去除重复项
    .forEach(System.out::println);

    // 需求5b:找出身高超过168的学生,要求去除重复的对象,再输出。
    students.stream().filter(s ->s.getHeight() > 168).distinct() //去除重复项
    .forEach(System.out::println);
    //若直接输出 发现distinct无法去除内容相同的重复对象
    //自定义类型的对象 如果希望内容一样就被认定为重复 需要重写hashCode和equals方法

    //需求6:合并Stream流
    Stream<String> st1 = Stream.of("A", "B");
    Stream<String> st2 = Stream.of("C", "D", "E");
    Stream<String> allSt = Stream.concat(st1, st2);
    allSt.forEach(System.out::println); //输出 A B C D E
  3. Stream流常见的终结方法

    • 终结方法指的是调用完成后,不会返回新Stream了,没法继续使用流了。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    List<Student> students =new ArrayList<>();
    Student s1 =new Student(name:"蜘蛛精", age: 26, height:172.5);
    Student s2 =new Student(name:"蜘蛛精", age:26, height:172.5);
    Student s3 = new Student(name:"紫霞", age:23, height:167.6);
    Student s4 = new Student(name:"白晶晶", age:25, height:169.0);
    Student s5 = new Student(name:"牛魔王", age: 35, height:183.3);
    Student s6 = new Student(name:"牛夫人", age:34, height:168.5);
    Collections.addAll(students, s1, s2, s3, s4, s5, s6);

    //需求1:计算出身高超过168的人数。
    long size = students.stream().filter(s -> s.getHeight() > 168).count(); //返回一个long

    //需求2:找出身高最高的学生对象。
    Student talleat = students.stream().max((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get;
    //比较Double类型的数字不能直接用减法 要用Double.compare 最后要用.get获取

    //需求3:找出身高最矮的学生对象。
    Student shortest = students.stream().min((o1, o2) -> Double.compare(o1.getHeight(), o2.getHeight())).get;
    • 收集Stream流:就是把Stream流操作后的结果转回到集合或者数组中去返回。
    • Stream流:方便操作集合/数组的手段,集合/数组 才是开发中的目的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    //!!!流只能收集一次!!!收集后这次流就关闭了!!!
    //需求4:请找出身高超过170的学生对象,并放到一个新集合中去返回。
    List<Student> studentsList = students.stream().filter(a -> a.hetHeight() > 170).collect(Collectors.toList());

    Set<Student> studentsSet = students.stream().filter(a -> a.hetHeight() > 170).collect(Collectors.toSet()); //去除重复对象

    //需求5:请找出身高超过170的学生对象,并把学生对象的名字和身高,存放到一个Map集合中去返回。
    Map<String, Double> studentsMap = students.stream().filter(a -> a.hetHeight() > 170)
    .distinct() //去除重复 否则有重复会报错
    .collect(Collectors.toMap(a -> a.getName(), a -> a.getHeight()));

    //需求6a:请找出身高超过170的学生对象,并把学生对象的身高,存放到一个数组中去返回。
    Object[] arr = student.stream().filter(a -> a.hetHeight() > 170).toArray();

    //需求6b:请找出身高超过170的学生对象,并把学生对象,存放到一个数组中去返回。
    Student[] arr = student.stream().filter(a -> a.hetHeight() > 170).toArray(stu -> new Student[stu]);