JavaSE(MAX-3)

文章目录

11 常用类

11.1 String⭐️

  • 基本介绍:
    • String 对象用于保存字符串,也就是一组字符序列

    • 字符串常量对象是用双引号括起的字符序列

    • 字符串的字符使用Unicode宇符编码,一个字符(不区分字母还是汉字)占两个字节

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jvgBVkN9-1692078080343)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/截屏2022-05-19 16.36.03.jpg)]

  • 创建String对象方式:
    1. 直接赋值 String s = “hsp”;
    2. 调用构造器 String s2 = new String(“hsp”);
  • 两种方式对比:
    • 方式一:先从常量池查看是否有”hsp”数据空间,如果有,直接指向;

      如果沒有则重新创建,然后指向。s最终指向的是常量池的空间地址

    • 方式二:先在堆中创建空间,里面维护了value属性,指向常量池的hsp空间。如果常量池没有"hsp“,重新创建,如果有,直接通过value指向,最终指向的是堆中的空间地址

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mvP5hh38-1692078080343)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-19%2016.54.53.jpg)]

  • 常用构造方法:
    • String s1 = new String();

    • String s2 = new String (String original);

    • String s3 = new String (char[] a);

    • String s4 = new String (char[] a,int startIndex,int count);

  • 注意事项和细节:
    1. String 是 final类,不能被继承
    2. string 有属性 private final char valve []; 用于存放字符串内容
    3. value 是一个final类型,不可以修改(值可以修改,地址不可修改)
    4. intern方法:返回常量池的地址对象
  • 特性:
    • 相等特性:
      1. equals判断内容

      2. ==判断地址

        public class StringExercise03 {
            public static void main(String[] args) {
                String a = "hsp"; //a 指向 常量池的 “hsp”
                String b =new String("hsp");//b 指向堆中对象
                System.out.println(a.equals(b)); //T
                System.out.println(a==b); //F
                //b.intern() 方法返回常量池地址
                System.out.println(a==b.intern()); //T //ntern方法:返回常量池的地址对象
                System.out.println(b==b.intern()); //F
        
            }
        }
        

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-anLWy4LW-1692078080344)(…/…/…/Library/Application%20Support/typora-user-images/%E6%88%AA%E5%B1%8F2022-09-14%2014.26.46.jpg)]

        public class StringExercise04 {
            public static void main(String[] args) {
                String s1 = "hspedu"; //指向常量池”hspedu”
                String s2 = "java"; //指向常量池”java”
                String s4 = "java";//指向常量池”java”
                String s3 = new String("java");//指向堆中对象
                System.out.println(s2 == s3); // F
                System.out.println(s2 == s4);  //T
                System.out.println(s2.equals(s3));//T
                System.out.println(s1 == s2);  //F
        
            }
        }
        

        [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ye8ScF00-1692078080344)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-09-14%2014.44.03.jpg)]

        public class StringExercise05 {
            public static void main(String[] args) {
                Person p1 = new Person();
                p1.name = "hspedu";
                Person p2 = new Person();
                p2.name = "hspedu";
        
                System.out.println(p1.name.equals(p2.name));//比较内容: True
                System.out.println(p1.name == p2.name);  //T
                System.out.println(p1.name == "hspedu");   //T
        
                String s1 = new String("bcde");
                String s2 = new String("bcde");
                System.out.println(s1==s2); //False
            }
        }
        class Person {
            public String name;
        }
        

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4981dDL6-1692078080344)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-19%2023.06.01.jpg)]

    • 字符串特性:
      1. String c1 =“ab”+"cd”;常量在池中相加

      2. String c1 = a + b;变量在堆中相加

        String s1 = "hello";
        s1 = "haha";
        
        //创建了两个对象,s1指向“hello”,
        //再在常量池中创建“haha”,s1指向"haha“
        
        String a = "hello" + "abc";
        
        //等价于 String a = "helloabc"
        //判断创建的常量池对象,是否有引用指向
        
        public class StringExercise08 {
            public static void main(String[] args) {
                String a = "hello"; //创建 a对象
                String b = "abc";//创建 b对象
                //老韩解读
                //1. 先 创建一个 StringBuilder sb = StringBuilder()
                //2. 执行  sb.append("hello");
                //3. sb.append("abc");
                //4. String c= sb.toString()
                //最后其实是 c 指向堆中的对象(String) value[] -> 池中 "helloabc"
                String c = a + b;
                String d = "helloabc";
                System.out.println(c == d);//F c在堆中
                String e = "hello" + "abc";//直接看池,e指向常量池
                System.out.println(d == e);//T
            }
        }
        
        /*底层:String Builder sb = new String Builder();
        sb.append(a);sb.append(b);
        sb是在堆中,井且append是在原来字符串的基础上追加的
        
        重要规则:String c1 =“ab”+"cd”;常量在池中相加
                String c1 = a + b;变量在堆中相加
        */
        

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iDrwaVg8-1692078080345)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-19%2022.58.20-20220914143425014.jpg)]

    public class StringExercise09 {
        public static void main(String[] args) {
            String s1 = "hspedu";  //s1 指向池中的 “hspedu”
            String s2 = "java"; // s2 指向池中的 “java”
            String s5 = "hspedujava"; //s5 指向池中的 “hspedujava”
            String s6 = (s1 + s2).intern();//s6 指向池中的   “hspedujava”
            System.out.println(s5 == s6); //T
            System.out.println(s5.equals(s6));//T
    
        }
    }
    
    • 综合训练
    class Test1 {
        String str = new String("hsp");
        final char[] ch = {'j', 'a', 'v', 'a'};
    
        public void change(String str, char ch[]) {
            str = "java";
            ch[0] = 'h';
        }
    
        public static void main(String[] args) {
            Test1 ex = new Test1();
            ex.change(ex.str, ex.ch);
            System.out.print(ex.str + " and ");
            System.out.println(ex.ch);
        }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UhvJAqN8-1692078080345)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-19%2023.21.28-20220525113711262.jpg)]

  • 常用方法:
    • equals:区分大小写,判断内容是否相等

    • equalslgnoreCase:忽略大小写的判断内容是否相等

    • length:获取字符的个数,字符串的长度

    • indexOf:获取字符在字符串中第1次出现的索引,索引从0开始,如果找不到,返回-1

    • lastlndexOf:获取宇符在字符串中最后1次出现的索引,索引从0开始,如找不到,返回-1

    • substring: 截取指定范围的子串

    • trim:去前后空格

    • charAt:获取某索引处的字符,注意不能使用Str[index]这种方式

    • toUpperCase:大写字母

    • toLowerCase:小写字母

    • concat:拼接

    • replace:替换字符串中的字符

    • split:分割字符串

    • compareTo:比较两个字符串的大小

    • toCharArray:转换成字符数组

    • format:格式字符串

      //1. equals 前面已经讲过了. 比较内容是否相同,区分大小写
              String str1 = "hello";
              String str2 = "Hello";
              System.out.println(str1.equals(str2));//
      
              // 2.equalsIgnoreCase 忽略大小写的判断内容是否相等
              String username = "johN";
              if ("john".equalsIgnoreCase(username)) {
                  System.out.println("Success!");
              } else {
                  System.out.println("Failure!");
              }
      
              // 3.length 获取字符的个数,字符串的长度
              System.out.println("韩顺平".length());
      
              // 4.indexOf 获取字符在字符串对象中第一次出现的索引,索引从0开始,如果找不到,返回-1
              String s1 = "wer@terwe@g";
              int index = s1.indexOf('@');
              System.out.println(index);// 3
              System.out.println("weIndex=" + s1.indexOf("we"));//0
      
              // 5.lastIndexOf 获取字符在字符串中最后一次出现的索引,索引从0开始,如果找不到,返回-1
              s1 = "wer@terwe@g@";
              index = s1.lastIndexOf('@');
              System.out.println(index);//11
              System.out.println("ter的位置=" + s1.lastIndexOf("ter"));//4
      
              // 6.substring 截取指定范围的子串
              String name = "hello,张三";
              //下面name.substring(6) 从索引6开始截取后面所有的内容
              System.out.println(name.substring(6));//截取后面的字符
              //name.substring(0,5)表示从索引0开始截取,截取到索引 5-1=4位置
              System.out.println(name.substring(2,5));//llo
      
      // 1.toUpperCase转换成大写
              String s = "heLLo";
              System.out.println(s.toUpperCase());//HELLO
      
              // 2.toLowerCase
              System.out.println(s.toLowerCase());//hello
      
              // 3.concat拼接字符串
      
              String s1 = "宝玉";
              s1 = s1.concat("林黛玉").concat("薛宝钗").concat("together");
              System.out.println(s1);//宝玉林黛玉薛宝钗together
      
              // 4.replace 替换字符串中的字符
              s1 = "宝玉 and 林黛玉 林黛玉 林黛玉";
              //在s1中,将 所有的 林黛玉 替换成薛宝钗
              // : s1.replace() 方法执行后,返回的结果才是替换过的.
              // 注意对 s1没有任何影响
              String s11 = s1.replace("宝玉", "jack");
              System.out.println(s1);//宝玉 and 林黛玉 林黛玉 林黛玉
              System.out.println(s11);//jack and 林黛玉 林黛玉 林黛玉
      
              // 5.split 分割字符串, 对于某些分割字符,我们需要 转义比如 | \\等
              String poem = "锄禾日当午,汗滴禾下土,谁知盘中餐,粒粒皆辛苦";
      
              //:
              // 1. 以 , 为标准对 poem 进行分割 , 返回一个数组
              // 2. 在对字符串进行分割时,如果有特殊字符,需要加入 转义符 \
              String[] split = poem.split(",");
              poem = "E:\\aaa\\bbb";
              split = poem.split("\\\\");
              System.out.println("==分割后内容===");
              for (int i = 0; i < split.length; i++) {
                  System.out.println(split[i]);
              }
      
              // 6.toCharArray 转换成字符数组
              s = "happy";
              char[] chs = s.toCharArray();
              for (int i = 0; i < chs.length; i++) {
                  System.out.println(chs[i]);
              }
      
              // 7.compareTo 比较两个字符串的大小,如果前者大,
              // 则返回正数,后者大,则返回负数,如果相等,返回0
              // 
              // (1) 如果长度相同,并且每个字符也相同,就返回 0
              // (2) 如果长度相同或者不相同,但是在进行比较时,可以区分大小
              //     就返回 if (c1 != c2) {
              //                return c1 - c2;
              //            }
              // (3) 如果前面的部分都相同,就返回 str1.len - str2.len
              String a = "jcck";// len = 3
              String b = "jack";// len = 4
              System.out.println(a.compareTo(b)); // 返回值是 'c' - 'a' = 2的值
      
              // 8.format 格式字符串
              /* 占位符有:
               * %s 字符串 %c 字符 %d 整型 %.2f 浮点型
               *
               */
              String name = "john";
              int age = 10;
              double score = 56.857;
              char gender = '男';
              //将所有的信息都拼接在一个字符串.
              String info =
                      "我的姓名是" + name + "年龄是" + age + ",成绩是" + score + 
                      "性别是" + gender + "。希望大家喜欢我!";
      
              System.out.println(info);
      
              //1. %s , %d , %.2f %c 称为占位符,这些占位符由后面变量来替换
              //2. %s 由 字符串来替换
              //3. %d 由 整数来替换
              //4. %.2f 表示使用小数来替换,替换后,只会保留小数点两位, 并且进行四舍五入的处理
              //5. %c 使用char 类型来替换
              String formatStr = "我的姓名是%s 年龄是%d,成绩是%.2f 性别是%c.希望大家喜欢我!";
      
              String info2 = String.format(formatStr, name, age, score, gender);
      
              System.out.println("info2=" + info2);
      

11.2 StringBuffer⭐️

  • 基本介绍:
    • java.lang.StringBuffer代表可变的字符序列,可以对字符串内容进行增删。
    • 很多方法与String相同,但StringBuffer是可变长度的。
    • String Buffer是一个容器。
  • 注意事项和细节:
    1. StringBuffer 的直接父类是 AbstractstringBuilder
    2. StringBuffer 实现了 Serializable,即StringBuffer的对象可以串行化
    3. 在父类中 AbstractstringBuilder 有属性 char[] valve,不是final,该 valve 数组存放 宁符串内容,引出存放在堆中的
    4. StringBuffer 是一个final类,不能被继承
    5. StringBuffer 字符内容存在 char[] value,所以在变化〔增加/删除)不用每次都更换地址(即不是每次创建新对象)
  • String和StringBuffer对比:
    1. String保存的是宇符串常量,里面的值不能更改,每次String类的更新实际上就是更改地址
    2. StringBuffer保存的是字符串变量,里面的值可以更改,不用更新地址,效率高
  • 构造器:
    //1. 创建一个 大小为 16的 char[] ,用于存放字符内容
    StringBuffer stringBuffer = new StringBuffer();
    
    //2 通过构造器指定 char[] 大小
    StringBuffer stringBuffer1 = new StringBuffer(100);
    
    //3. 通过 给一个String 创建 StringBuffer, char[] 大小就是 str.length() + 16
    StringBuffer hello = new StringBuffer("hello");
    
  • String和StringBuffer转换:
    // String——>StringBuffer
    String str = "hello tom";
    
    //方式1 使用构造器
    //注意: 返回的才是StringBuffer对象,对str 本身没有影响
    StringBuffer stringBuffer = new StringBuffer(str);
    //方式2 使用的是append方法
    StringBuffer stringBuffer1 = new StringBuffer();
    stringBuffer1 = stringBuffer1.append(str);
    
    // StringBuffer ->String
    StringBuffer stringBuffer3 = new StringBuffer("韩顺平教育");
    //方式1 使用StringBuffer提供的 toString方法
    String s = stringBuffer3.toString();
    //方式2: 使用构造器来搞定
    String s1 = new String(stringBuffer3);
    
  • 常用方法:
    • 增:append

    • 删:delete(start,end)

    • 改:replace (start,end,string)

    • 查:indexOf

    • 插:insert

    • 获取长度:length

      StringBuffer s = new StringBuffer("hello");
      
      //增
      s.append(',');// "hello,"
      s.append("张三丰");//"hello,张三丰"
      s.append("赵敏").append(100).append(true).append(10.5);//"hello,张三丰赵敏100true10.5"
      System.out.println(s);//"hello,张三丰赵敏100true10.5"
      
      //删
      /*
      * 删除索引为>=start && <end 处的字符
      * : 删除 11~14的字符 [11, 14)
      */
      s.delete(11, 14);
      System.out.println(s);//"hello,张三丰赵敏true10.5"
      
      //改
      //,使用 周芷若 替换 索引9-11的字符 [9,11)
      s.replace(9, 11, "周芷若");
      System.out.println(s);//"hello,张三丰周芷若true10.5"
      //查找指定的子串在字符串第一次出现的索引,如果找不到返回-1
      int indexOf = s.indexOf("张三丰");
      System.out.println(indexOf);//6
      
      //插
      //,在索引为9的位置插入 "赵敏",原来索引为9的内容自动后移
      s.insert(9, "赵敏");
      System.out.println(s);//"hello,张三丰赵敏周芷若true10.5"
      
      //长度
      System.out.println(s.length());//22
      System.out.println(s);
      

11.3 StringBulider⭐️

  • 基本介绍:
    • 一个可变的字符序列。此类提供一个与 StringBuffer 兼容的 API,但不保证同步,
    • 该类被设计用作 String Buffer 的个简易替换,用在字符串缓冲区被单个线程使用的时候
    • 如果可能,建议优先采用该类,因为在大多数实现中,它比StringBuffer 快
    • 在 StringBuilder 上的主要操作是 append 和 insert 方法,可重载这些方法,以接受任意类型的数据
  • 注意事项和细节:
    1. StringBuilder 继承 AbstractstringBuilder 类
    2. 实现了 Serializable,说明StringBuilder对象是可以串行化(对象可以网络传输,可以保存到文件)
    3. StringBuilder 是final类,不能被继承
    4. StringBuitder对象字符序列仍然足存放在其父类 AbstractstringBuilder的 char [I valve;因此,字符序列是堆中
    5. StringBoilder 的方法,没有做互斥的处理,即没有synchronized 关键字,网此在单线程的情況下使用
  • 常用方法:
    • 和StringBuffer相同
  • 三类对比:
    1. String:不可变字符序列,效率低,但是复用率高
    2. StringBuffer:可变字符序列,效率较高(增删),线程安全
    3. String Builder:可变字符序列、效率最高、线程不安全
    4. StringBuilder 和 StringBuffer 非常类似,均代表可变的字符序列,且方法相同
    5. String使用注意说明:string s="a”;//创建了一个字符串s+=“b”;1/实际上原来的"a"字符串对象已经丢奔了,现在又产生了一个字符串S+“b”(也就是”ab”)。如果多次执行这些改变串内容的操作,会导致大量副本字符串对象存留在内存中,降低效率。如果这样的操作放到循环中,会极大影响程序的性能
  • 三类选择:
    1. 如果宇符串存在大量的修改操作,一般使用 StringBuffer 或StringBuilder
    2. 如果字符串存在大量的修改操作,并在单线程的情況,使用 StringBuilder
    3. 如果字符串存在大量的修改操作,并在多线程的情况,使用 String Buffer
    4. 如果我们字符串很少修改,被多个对象引用,使用String

11.4 Math

  • 基本介绍:

    Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数,均为静态方法

  • 常用方法:
    • abs:绝对值
    • pow:求幂
    • ceil:向上取整
    • floor:向下取整
    • round:四舍五入
    • sqrt:求开方
    • random:求随机数
    • max:求两个数的最大值
    • min:求两个数的最小值

11.5 Date、Calenda、LocalDate

11.5.1 Date

  • 常用方法:
    • Date:精确到毫秒,代表特定时间

    • Simple Date Format:格式和解析日期的类SimpleDateFormat 格式化和解析日期的具体类

      它允许进行格式化(日期一>文本)解析(文本->日期)和规范化

11.5.2 Calenda

  • 基本介绍:

    是一个抽象类,并且构造器是private,可以通过 getInstance()来获得实例,提供大量的方法和字段

  • 常用方法:
    • getInstance():获取实例
    • get.XXXX:获取字段

11.5.3 LocalDate

  • 常用方法:
    • LocalDateTime ldt = LocalDateTime.now():获取时间

    • get.Xxxx():获取字段

    • DateTimeFormatter.format():格式化

    • DateTimeFormatter.ofPattern():定制格式化

    • Date date = Date.from(instant);:Instant转换Date

    • Instant instant = date.tolnstant();:Date转换Instant

      //1. 使用now() 返回表示当前日期时间的 对象
      LocalDateTime ldt = LocalDateTime.now(); //LocalDate.now();//LocalTime.now()
      System.out.println(ldt);
      
      //2. 使用DateTimeFormatter 对象来进行格式化
      // 创建 DateTimeFormatter对象
      DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
      String format = dateTimeFormatter.format(ldt);
      System.out.println("格式化的日期=" + format);
      
      System.out.println("年=" + ldt.getYear());
      System.out.println("月=" + ldt.getMonth());
      System.out.println("月=" + ldt.getMonthValue());
      System.out.println("日=" + ldt.getDayOfMonth());
      System.out.println("时=" + ldt.getHour());
      System.out.println("分=" + ldt.getMinute());
      System.out.println("秒=" + ldt.getSecond());
      
      LocalDate now = LocalDate.now(); //可以获取年月日
      LocalTime now2 = LocalTime.now();//获取到时分秒
      
      
      //提供 plus 和 minus方法可以对当前时间进行加或者减
      //看看890天后,是什么时候 把 年月日-时分秒
      LocalDateTime localDateTime = ldt.plusDays(890);
      System.out.println("890天后=" + dateTimeFormatter.format(localDateTime));
      
      //看看在 3456分钟前是什么时候,把 年月日-时分秒输出
      LocalDateTime localDateTime2 = ldt.minusMinutes(3456);
      System.out.println("3456分钟前 日期=" + dateTimeFormatter.format(localDateTime2));
      

11.6 System

  • 常用方法:
    • exit:退出当前程序
    • arraycopy:复制数组元素,比较适合底层调用,一般使用Arrays.copyOf完成复制数组
      int[] src={1,2.3}:
      int[] dest = new int[3];
      System.arraycopy (src, 0, dest, 0, 3);
    • currentTimeMillens:返回当前时间距离1970-1-1的毫秒数
    • gc():运行垃圾回收机制

11.7 Arrays

  • 基本介绍:

    Arrays里面包含了一系列静态方法,用于管理或操作数组(比如排序和搜索)

  • 常用方法:
    • toString:返回数组的字符串形式:

      Arrays.toString (arr)

    • sort:排序(自然排序和定制排序):

      Arrays.sout(arr)

    • binarySearch:通过二分搜索法进行查找,必须是有排序的数组:

      int index = Arrays.binarySearch (arr, 需要查找的数)

    • copyOf:数组元素的复制:

      Integer[] newArr = Arrays.copyOf(arr, arr.length)

    • fill:数组元素的填充
      Integer[] num = new Integer[]{9,3,2}
      Arrays.fill (num, 99);

    • equals:比较两个数组元素内容是否完全一
      boolean equals = Arrays.equals(arr. arr2)

    • aslist:将一组值,转换成list
      List asList = Arrays.asList(2,3,4,5,6,1)

      Integer[] arr = {1, 2, 90, 123, 567};
      // binarySearch 通过二分搜索法进行查找,要求必须排好
      // 
      //1. 使用 binarySearch 二叉查找
      //2. 要求该数组是有序的. 如果该数组是无序的,不能使用binarySearch
      //3. 如果数组中不存在该元素,就返回 return -(low + 1);  // key not found.
      int index = Arrays.binarySearch(arr, 567);
      System.out.println("index=" + index);
      
      //copyOf 数组元素的复制
      // 
      //1. 从 arr 数组中,拷贝 arr.length个元素到 newArr数组中
      //2. 如果拷贝的长度 > arr.length 就在新数组的后面 增加 null
      //3. 如果拷贝长度 < 0 就抛出异常NegativeArraySizeException
      //4. 该方法的底层使用的是 System.arraycopy()
      Integer[] newArr = Arrays.copyOf(arr, arr.length);
      System.out.println("==拷贝执行完毕后==");
      System.out.println(Arrays.toString(newArr));
      
      //ill 数组元素的填充
      Integer[] num = new Integer[]{9,3,2};
      //
      //1. 使用 99 去填充 num数组,可以理解成是替换原理的元素
      Arrays.fill(num, 99);
      System.out.println("==num数组填充后==");
      System.out.println(Arrays.toString(num));
      
      //equals 比较两个数组元素内容是否完全一致
      Integer[] arr2 = {1, 2, 90, 123};
      //
      //1. 如果arr 和 arr2 数组的元素一样,则方法true;
      //2. 如果不是完全一样,就返回 false
      boolean equals = Arrays.equals(arr, arr2);
      System.out.println("equals=" + equals);
      
      //asList 将一组值,转换成list
      //
      //1. asList方法,会将 (2,3,4,5,6,1)数据转成一个List集合
      //2. 返回的 asList 编译类型 List(接口)
      //3. asList 运行类型 java.util.Arrays#ArrayList, 是Arrays类的
      //   静态内部类 private static class ArrayList<E> extends AbstractList<E>
      //              implements RandomAccess, java.io.Serializable
      List asList = Arrays.asList(2,3,4,5,6,1);
      System.out.println("asList=" + asList);
      System.out.println("asList的运行类型" + asList.getClass());
      

11.8 BigInteger BigDecimal

  • 基本介绍:
    • Biglnteger适合保存比较大的整型

    • BigDecimal适合保存精度更高的浮点型(小数)

  • 常用方法:
    • add:加
    • subtract:减
    • multiply:乘
    • divide:除

12 集合

  • 基本介绍:
    • 可以动态保存任意多个对象
    • 提供了一系列方便的操作对象的方法
    • 使用集合添加、删除新元素的示意代码更简洁

12.1 集合框架体系⭐️

  • 框架图:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5MTnxsM9-1692078080346)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-23%2020.29.58.jpg)]

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IwsLJxL9-1692078080346)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-23%2020.31.21.jpg)]

12.2 Collection

  • 基本介绍:
    • Collection实现子类可以存放多个元素,每个元素可以是Object
    • Collection的实现类,有些可以存放重复的元素,有些不可以
    • Collection的实现类,有些是有序的(List),有些不是有序(Set)
    • Collection接口没有直接的实现子类,是通过它的子接口Set 和 List 来实现的
  • 接口常用方法:
    • add:添加单个元素

    • remove:删除指定元素

    • contains:查找元素是否存在

    • size:获取元素个数

    • isEmpty:判断是否为空

    • clear:清空

    • addAll:添加多个元素

    • containsAll:查找多个元素是否都存在

    • removeAll:删除多个元素

      List list = new ArrayList();
      //        add:添加单个元素
              list.add("jack");
              list.add(10);//list.add(new Integer(10))
              list.add(true);
              System.out.println("list=" + list);
      //        remove:删除指定元素
              //list.remove(0);//删除第一个元素
              list.remove(true);//指定删除某个元素
              System.out.println("list=" + list);
      //        contains:查找元素是否存在
              System.out.println(list.contains("jack"));//T
      //        size:获取元素个数
              System.out.println(list.size());//2
      //        isEmpty:判断是否为空
              System.out.println(list.isEmpty());//F
      //        clear:清空
              list.clear();
              System.out.println("list=" + list);
      //        addAll:添加多个元素
              ArrayList list2 = new ArrayList();
              list2.add("红楼梦");
              list2.add("三国演义");
              list.addAll(list2);
              System.out.println("list=" + list);
      //        containsAll:查找多个元素是否都存在
              System.out.println(list.containsAll(list2));//T
      //        removeAll:删除多个元素
              list.add("聊斋");
              list.removeAll(list2);
              System.out.println("list=" + list);//[聊斋]
      //        说明:以ArrayList实现类来演示.
      

12.2.1 List

  • 基本介绍:
    • List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复
    • List集合中的每个元素都有其对应的顺序索引,即支持索引
    • List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器元素
    • 常用: ArrayList、LinkedList、Vector
  • 常用方法:
    • void add(int index, Object ele):在index位置插入ele元素

    • boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来

    • Object get(int index):获取指定index位置的元素

    • int indexOf(Object obj):返回obj在集合中首次出现的位置

    • int lastindexOf(Object obj):返回obj在当前集合中末次出现的位置

    • Object remove(int index):移除指定index位置的元素,井返回此元素

    • Object set(int index, Object ele):设置指定index位置的元素为ele,相当于是替换

    • List sublist(int fromlndex, int tolndex):返回从fromlndex到tolndex位置的子集合

      List list = new ArrayList();
              list.add("张三丰");
              list.add("贾宝玉");
      //        void add(int index, Object ele):在index位置插入ele元素
              //在index = 1的位置插入一个对象
              list.add(1, "韩顺平");
              System.out.println("list=" + list);
      
      //        boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
              List list2 = new ArrayList();
              list2.add("jack");
              list2.add("tom");
              list.addAll(1, list2);
              System.out.println("list=" + list);
      
      //        Object get(int index):获取指定index位置的元素
      
      //        int indexOf(Object obj):返回obj在集合中首次出现的位置
              System.out.println(list.indexOf("tom"));//2
      
      //        int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置
              list.add("韩顺平");
              System.out.println("list=" + list);
              System.out.println(list.lastIndexOf("韩顺平"));
      
      //        Object remove(int index):移除指定index位置的元素,并返回此元素
              list.remove(0);
              System.out.println("list=" + list);
      
      //        Object set(int index, Object ele):设置指定index位置的元素为ele , 相当于是替换.
              list.set(1, "玛丽");
              System.out.println("list=" + list);
      
      //        List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
              // 注意返回的子集合 fromIndex <= subList < toIndex
              List returnlist = list.subList(0, 2);
              System.out.println("returnlist=" + returnlist);
      
12.2.1.1 迭代器
  • 基本介绍:
    • lterator对象称为迭代器,主要用于遍历 Collection 集合中的元素

    • 实现了Collection接口的集合都有一个iterator()方法,用以返回一个实现了lterator接口的对象,即可以返回一个迭代器

    • lterator 仅用于遍历集合,lterator 本身并不存放对象

    • 在调用it.next()方法之前必须要调用it.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出NoSuchElementException异常

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-31Y2CufA-1692078080347)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-24%2017.23.58.jpg)]

      public class CollectionIterator {
          @SuppressWarnings({"all"})
          public static void main(String[] args) {
      
              Collection col = new ArrayList();
      
              col.add(new Book("三国演义", "罗贯中", 10.1));
              col.add(new Book("小李飞刀", "古龙", 5.1));
              col.add(new Book("红楼梦", "曹雪芹", 34.6));
      
              //System.out.println("col=" + col);
              //现在老师希望能够遍历 col集合
              //1. 先得到 col 对应的 迭代器
              Iterator iterator = col.iterator();
              //2. 使用while循环遍历
      //        while (iterator.hasNext()) {//判断是否还有数据
      //            //返回下一个元素,类型是Object
      //            Object obj = iterator.next();
      //            System.out.println("obj=" + obj);
      //        }
            
              //老师教大家一个快捷键,快速生成 while => itit
              //显示所有的快捷键的的快捷键 ctrl + j
            
              while (iterator.hasNext()) {
                  Object obj = iterator.next();
                  System.out.println("obj=" + obj);
      
              }
              //3. 当退出while循环后 , 这时iterator迭代器,指向最后的元素
              //   iterator.next();//NoSuchElementException
              //4. 如果希望再次遍历,需要重置我们的迭代器
              iterator = col.iterator();
              System.out.println("===第二次遍历===");
              while (iterator.hasNext()) {
                  Object obj = iterator.next();
                  System.out.println("obj=" + obj);
      
              }
          }
      }
      
      class Book {
          private String name;
          private String author;
          private double price;
      
          public Book(String name, String author, double price) {
              this.name = name;
              this.author = author;
              this.price = price;
          }
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public String getAuthor() {
              return author;
          }
      
          public void setAuthor(String author) {
              this.author = author;
          }
      
          public double getPrice() {
              return price;
          }
      
          public void setPrice(double price) {
              this.price = price;
          }
      
          @Override
          public String toString() {
              return "Book{" +
                      "name='" + name + '\'' +
                      ", author='" + author + '\'' +
                      ", price=" + price +
                      '}';
          }
      }
      
12.2.1.2 for增强循环
  • 基本介绍:

    增强for循环,可以代替iterator迭代器,特点:增强for就是简化版的iterator,本质一样,只能用于遍历集合或数组

  • 基本语法:
    for(元素类型 元素名: 集合名或数组名){
      访问元素;
    }
    
12.2.1.3 普通遍历循环
12.2.1.4 ArrayList⭐️
  • 注意事项和细节:
    1. 允许所有元素包括null加入
    2. ArrayList 是由数组来实现数据存储的
    3. ArrayList 基本等同于Vector,除了 ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList
  • 底层结构和源码分析:
    1. ArrayList中维护了一个Object类型的数组elementData,transient Object[] elementData;

      transient 表示瞬间,短暂的,表示该属性不会被序列化

    2. 创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData 为1.5倍

    3. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,则直接扩容elementData为1.5倍

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-72u6W7gJ-1692078080347)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-25%2011.38.41.jpg)]

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nTyPT8Vj-1692078080348)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-25%2011.40.06.jpg)]

12.2.1.5 Vector⭐️
  • 注意事项和细节:
    1. Vector底层是一个对象数组, protected Object[] elementData;
    2. Vector 是线程同步的,即线程安全,Vector类的操作方法带有synchronized
  • 底层机制和源码分析:
    public class Vector_ {
        public static void main(String[] args) {
            //无参构造器
            //有参数的构造
            Vector vector = new Vector(8);
            for (int i = 0; i < 10; i++) {
                vector.add(i);
            }
            vector.add(100);
            System.out.println("vector=" + vector);
            //老韩解读源码
            //1. new Vector() 底层
            /*
                public Vector() {
                    this(10);
                }
             补充:如果是  Vector vector = new Vector(8);
                走的方法:
                public Vector(int initialCapacity) {
                    this(initialCapacity, 0);
                }
             2. vector.add(i)
             2.1  //下面这个方法就添加数据到vector集合
                public synchronized boolean add(E e) {
                    modCount++;
                    ensureCapacityHelper(elementCount + 1);
                    elementData[elementCount++] = e;
                    return true;
                }
              2.2  //确定是否需要扩容 条件 : minCapacity - elementData.length>0
                private void ensureCapacityHelper(int minCapacity) {
                    // overflow-conscious code
                    if (minCapacity - elementData.length > 0)
                        grow(minCapacity);
                }
              2.3 //如果 需要的数组大小 不够用,就扩容 , 扩容的算法
                  //newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                  //                             capacityIncrement : oldCapacity);
                  //就是扩容两倍.
                private void grow(int minCapacity) {
                    // overflow-conscious code
                    int oldCapacity = elementData.length;
                    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                                     capacityIncrement : oldCapacity);
                    if (newCapacity - minCapacity < 0)
                        newCapacity = minCapacity;
                    if (newCapacity - MAX_ARRAY_SIZE > 0)
                        newCapacity = hugeCapacity(minCapacity);
                    elementData = Arrays.copyOf(elementData, newCapacity);
                }
             */
        }
    }
    
  • ArrayList和Vector:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fWceaCRv-1692078080348)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-26%2011.43.18.jpg)]

12.2.1.6 LinkedList
  • 注意事项和细节:
    1. LinkedList底层实现了双向链表和双端队列特点
    2. 可以添加任意元素包括null
    3. 线程不安全,没有实现同步
  • 底层机制:
    1. Linkedlist底层维护了一个双向链表

    2. Linkedlist中维护了两个属性first和last分别指向首节点和尾节点

    3. 每个节点(Node对象),里面又维护了prev、next.item三个属性,其中通过
      prev指向前一个,通过next指向后一个节点。最终实现双向链表

    4. 所以LinkedList的元素的添加和删除,不是通过数组完成的,相对来说效率较高

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q7t7iT3D-1692078080348)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-26%2019.18.01.jpg)]

  • ArrayList和LinkedList:
    1. 如果我们改查的操作多,选择ArrayList

    2. 如果我们增删的操作多,选择LinkedList

    3. 一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yNYr9ZZI-1692078080349)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-26%2019.20.03.jpg)]

12.2.2 Set

  • 基本介绍:
    • 无序(添加和取出的顺序不一致),没有索引
    • 不允许重复元素,所以最多包含一个null
    • JDK API中Set接口的实现类有:
      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rmchhHg6-1692078080349)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-26%2019.21.30.jpg)]
  • 常用方法:
    • add:添加单个元素

    • remove:删除指定元素

    • contains:查找元素是否存在

    • size:获取元素个数

    • isEmpty:判断是否为空

    • clear:清空

    • addAll:添加多个元素

    • containsAll:查找多个元素是否都存在

    • removeAll:删除多个元素

      public class SetMethod {
          public static void main(String[] args) {
              //老韩解读
              //1. 以Set 接口的实现类 HashSet 来讲解Set 接口的方法
              //2. set 接口的实现类的对象(Set接口对象), 不能存放重复的元素, 可以添加一个null
              //3. set 接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
              //4. 注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定.
              Set set = new HashSet();
              set.add("john");
              set.add("lucy");
              set.add("john");//重复
              set.add("jack");
              set.add("hsp");
              set.add("mary");
              set.add(null);//
              set.add(null);//再次添加null
              for(int i = 0; i <10;i ++) {
                  System.out.println("set=" + set);
              }
      
              //遍历
              //方式1: 使用迭代器
              System.out.println("=====使用迭代器====");
              Iterator iterator = set.iterator();
              while (iterator.hasNext()) {
                  Object obj =  iterator.next();
                  System.out.println("obj=" + obj);
      
              }
      
              set.remove(null);
      
              //方式2: 增强for
              System.out.println("=====增强for====");
      
              for (Object o : set) {
                  System.out.println("o=" + o);
              }
              //set 接口对象,不能通过索引来获取
          }
      }
      
  • 遍历方式:
    1. 迭代器
    2. 增强for
    3. 不能使用索引方式获取(普通遍历循环)
12.2.2.1 HashSet⭐️
  • 注意事项和细节:
    1. Hashset实现了Set接口
    2. Hashset实际上是HashMap
    3. 可以存放null值,但是只能有一个null
    4. Hashset不保证元素是有序的,取决于hash后,再确定索引的结果
    5. 不能有重复元素/对象
  • 底层机制和源码分析:
    1. HashSet 底层是 HashMap

    2. 添加一个元素时,先得到hash值会转成索引值

    3. 找到存储数据表table,看这个素引位置是否己经存放的有元素如果没有,直接加入

    4. 如果有调用equals 比较,如果相同,就放奔添加,如果不相同,则添加到最后

    5. 在Java8中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),井且table的大小>=MIN TREEIFY CAPACITY(默认64)就会进行树化(红黑树)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q3r3BFwb-1692078080350)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-27%2011.48.11.jpg)]

      public class HashSetSource {
          public static void main(String[] args) {
      
              HashSet hashSet = new HashSet();
              hashSet.add("java");//到此位置,第1次add分析完毕.
              hashSet.add("php");//到此位置,第2次add分析完毕
              hashSet.add("java");
              System.out.println("set=" + hashSet);
      
              /*
              HashSet 的源码解读
              1. 执行 HashSet()
                  public HashSet() {
                      map = new HashMap<>();
                  }
              2. 执行 add()
                 public boolean add(E e) {//e = "java"
                      return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
                 }
               3.执行 put() , 该方法会执行 hash(key) 得到key对应的hash值 算法h = key.hashCode()) ^ (h >>> 16)
                   public V put(K key, V value) {//key = "java" value = PRESENT 共享
                      return putVal(hash(key), key, value, false, true);
                  }
               4.执行 putVal
               final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                         boolean evict) {
                      Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量
                      //table 就是 HashMap 的一个数组,类型是 Node[]
                      //if 语句表示如果当前table 是null, 或者 大小=0
                      //就是第一次扩容,到16个空间.
                      if ((tab = table) == null || (n = tab.length) == 0)
                          n = (tab = resize()).length;
      
                      //(1)根据key,得到hash 去计算该key应该存放到table表的哪个索引位置
                      //并把这个位置的对象,赋给 p
                      //(2)判断p 是否为null
                      //(2.1) 如果p 为null, 表示还没有存放元素, 就创建一个Node (key="java",value=PRESENT)
                      //(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null)
      
                      if ((p = tab[i = (n - 1) & hash]) == null)
                          tab[i] = newNode(hash, key, value, null);
                      else {
                          //一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建
                          Node<K,V> e; K k; //
                          //如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
                          //并且满足 下面两个条件之一:
                          //(1) 准备加入的key 和 p 指向的Node 结点的 key 是同一个对象
                          //(2)  p 指向的Node 结点的 key 的equals() 和准备加入的key比较后相同
                          //就不能加入
                          if (p.hash == hash &&
                              ((k = p.key) == key || (key != null && key.equals(k))))
                              e = p;
                          //再判断 p 是不是一颗红黑树,
                          //如果是一颗红黑树,就调用 putTreeVal , 来进行添加
                          else if (p instanceof TreeNode)
                              e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                          else {//如果table对应索引位置,已经是一个链表, 就使用for循环比较
                          
                                //(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后
                                //    注意在把元素添加到链表后,立即判断 该链表是否已经达到8个结点
                                //    , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
                                //    注意,在转成红黑树时,要进行判断, 判断条件
                                //    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
                                //            resize();
                                //    如果上面条件成立,先table扩容.
                                //    只有上面条件不成立时,才进行转成红黑树
                                //(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break
      
                              for (int binCount = 0; ; ++binCount) {
                                  if ((e = p.next) == null) {
                                      p.next = newNode(hash, key, value, null);
                                      if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
                                          treeifyBin(tab, hash);
                                      break;
                                  }
                                  if (e.hash == hash &&
                                      ((k = e.key) == key || (key != null && key.equals(k))))
                                      break;
                                  p = e;
                              }
                          }
                          if (e != null) { // existing mapping for key
                              V oldValue = e.value;
                              if (!onlyIfAbsent || oldValue == null)
                                  e.value = value;
                              afterNodeAccess(e);
                              return oldValue;
                          }
                      }
                      ++modCount;
                      //size 就是我们每加入一个结点Node(k,v,h,next), size++
                      if (++size > threshold)
                          resize();//扩容
                      afterNodeInsertion(evict);
                      return null;
                  }
               */
          }
      }
      
  • 扩容和红黑树机制:
    1. HashSet底层是HashMap
    2. 第一次添加时,table 数组扩容到 16,临界值(threshold)是 16*加载因子(loadFactor)是0.75= 12
    3. 每加入一个节点,size就会++,到达临界值就会扩容
    4. 如果table 数组使用到了临界值 12,就会扩容到16*2=32,新的临界值就是32*0.75=24,依次类推
    5. 在Java8中,如果一条链表的元素个数到达 TREEIFY_ THRESHOLD(默认是 8)井且table的大小>=MIN TREEIFY CAPACITY (默认64),就会进行树化(红黑树),否则仍然采用数组扩容机制
  • 去重机制对比:
    • HashSet去重机制: hashCode() + equals(),底层先通过存入对象,通过运算hash值得到对应的索引,如果table索引所在的位置没有数据就直接存放;如果有数据就进行equals(注意重写情况)比较[遍历比较],如果比较后,不相同就加入,否则就不加入
    • TreeSet的去重机制:如果你传入了一个Comparator匿名对象,就使用实现的compare去重,如果方法返回0,就是相同的元素/数据,就不添加,如果你没有传入一个Comparator匿名对象,则以你添加的对象实现的Compareable接口的compareTo去重
  • 例:
    public class Homework06 {
        public static void main(String[] args) {
            HashSet set = new HashSet();//ok
            Person p1 = new Person(1001,"AA");//ok
            Person p2 = new Person(1002,"BB");//ok
            set.add(p1);//ok
            set.add(p2);//ok
            p1.name = "CC";
            set.remove(p1);
            System.out.println(set);//2
            set.add(new Person(1001,"CC"));
            System.out.println(set);//3
            set.add(new Person(1001,"AA"));
            System.out.println(set);//4
    
        }
    }
    
    class Person {
        public String name;
        public int id;
    
        public Person(int id, String name) {
            this.name = name;
            this.id = id;
        }
    
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            Person person = (Person) o;
            return id == person.id &&
                    Objects.equals(name, person.name);
        }
    
        @Override
        public int hashCode() {
            return Objects.hash(name, id);
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    '}';
        }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MpdVw6do-1692078080350)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-30%2011.03.03.jpg)]

12.2.2.2 LinkedHashSet
  • 注意事项和细节:
    1. LinkedHashset 是Hashset 的子类

    2. LinkedHashSet 底层是一个 LinkedHashMap,底层维护了一个 数组+双向链表

    3. LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,同时使用链表维护元素的次序(图),这使得元素看起来是以插入顺序保存的

    4. LinkedHashSet 不允许添重复元素

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Q7kf3Zh-1692078080351)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-29%2016.49.06.jpg)]

  • 底层机制和源代码分析:
    1. LinkedHashSet 加入顺序和取出元素/数据的顺序一致
    2. LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
    3. LinkedHashSet 底层结构 (数组table+双向链表)
    4. 添加第一次时,直接将 数组table 扩容到 16 ,存放的结点类型是 LinkedHashMap$Entry
    5. 数组是 HashMap N o d e [ ] 存放的元素 / 数据是 L i n k e d H a s h M a p Node[] 存放的元素/数据是 LinkedHashMap Node[]存放的元素/数据是LinkedHashMapEntry类型
    public class LinkedHashSetSource {
        public static void main(String[] args) {
            //分析一下LinkedHashSet的底层机制
            Set set = new LinkedHashSet();
            set.add(new String("AA"));
            set.add(456);
            set.add(456);
            set.add(new Customer("刘", 1001));
            set.add(123);
            set.add("HSP");
    
            System.out.println("set=" + set);
            
            //1. LinkedHashSet 加入顺序和取出元素/数据的顺序一致
            //2. LinkedHashSet 底层维护的是一个LinkedHashMap(是HashMap的子类)
            //3. LinkedHashSet 底层结构 (数组table+双向链表)
            //4. 添加第一次时,直接将 数组table 扩容到 16 ,存放的结点类型是 LinkedHashMap$Entry
            //5. 数组是 HashMap$Node[] 存放的元素/数据是 LinkedHashMap$Entry类型
            /*
                    //继承关系是在内部类完成.
                    static class Entry<K,V> extends HashMap.Node<K,V> {
                        Entry<K,V> before, after;
                        Entry(int hash, K key, V value, Node<K,V> next) {
                            super(hash, key, value, next);
                        }
                    }
             */
        }
    }
    class Customer {
        private String name;
        private int no;
    
        public Customer(String name, int no) {
            this.name = name;
            this.no = no;
        }
    }
    
12.2.2.3 TreeSet
  • 底层机制:
    1. TreeSet()构造器需传入Comparator接口的匿名内部类,因为底层 Comparable<? super K> k = (Comparator<? super K>) key;

      若没有传入,则需要把传入的类实现Comparable接口

    2. 若按照compare方法比较value相同则无法加入value

    public class TreeSet_ {
        public static void main(String[] args) {
    
            //1. 当我们使用无参构造器,创建TreeSet时,默认按字母排序
            //2. 老师希望添加的元素,按照字符串大小来排序
            //3. 使用TreeSet 提供的一个构造器,可以传入一个比较器(匿名内部类)
            //   并指定排序规则
            //4. 简单看看源码
            
            /*
            1. 构造器把传入的比较器对象,赋给了 TreeSet的底层的 TreeMap的属性this.comparator
    
             public TreeMap(Comparator<? super K> comparator) {
                    this.comparator = comparator;
                }
             2. 在 调用 treeSet.add("tom"), 在底层会执行到
    
                 if (cpr != null) {//cpr 就是我们的匿名内部类(对象)
                    do {
                        parent = t;
                        //动态绑定到我们的匿名内部类(对象)compare
                        cmp = cpr.compare(key, t.key);
                        if (cmp < 0)
                            t = t.left;
                        else if (cmp > 0)
                            t = t.right;
                        else //如果相等,即返回0,这个Key就没有加入
                            return t.setValue(value);
                    } while (t != null);
                }
             */
    
    //        TreeSet treeSet = new TreeSet();
            TreeSet treeSet = new TreeSet(new Comparator() {
                @Override
                public int compare(Object o1, Object o2) {
                    //下面 调用String的 compareTo方法进行字符串大小比较
                    //如果老韩要求加入的元素,按照长度大小排序
                    //return ((String) o2).compareTo((String) o1);
                    return ((String) o1).length() - ((String) o2).length();
                }
            });
            //添加数据.
            treeSet.add("jack");
            treeSet.add("tom");//3
            treeSet.add("sp");
            treeSet.add("a");
            treeSet.add("abc");//3
            
            System.out.println("treeSet=" + treeSet);
        }
    }
    

12.3 Map

  • 注意事项和细节:
    1. Map与Collection井列存在,用于保存具有映射关系的数据

    2. Map 中的key 和 value 可以是任何引用类型的数据,会封装到HashMap$Node对象中

    3. Map 中的key 不允许重复,原因和HashSet 一样

    4. Map 中的value 可以重复

    5. Map 的key可以为null,value也可以为null,key为null只有能有一个,value为null可以为多个

    6. 常用String类作为Map的key

    7. key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到对应的 value

    8. Map存放数据的key-value示意图,一对 k-y是放在一个Node中的,有因为Node 实现了 Entry 接口

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zyuMYNo3-1692078080351)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-27%2023.07.17.jpg)]

  • 常用方法:
    • put:添加

    • remove:根据键删除映射关系

    • get:根据键获取值

    • size:获取元素个数

    • isEmpty:判断个数是否为0

    • clear:清除

    • containskey:查找键是否存在

      public class MapMethod {
          public static void main(String[] args) {
              //演示map接口常用方法
      
              Map map = new HashMap();
              map.put("邓超", new Book("", 100));//OK
              map.put("邓超", "孙俪");//替换-> 一会分析源码
              map.put("王宝强", "马蓉");//OK
              map.put("宋喆", "马蓉");//OK
              map.put("刘令博", null);//OK
              map.put(null, "刘亦菲");//OK
              map.put("鹿晗", "关晓彤");//OK
              map.put("hsp", "hsp的老婆");
      
              System.out.println("map=" + map);
      
      //        remove:根据键删除映射关系
              map.remove(null);
              System.out.println("map=" + map);
      //        get:根据键获取值
              Object val = map.get("鹿晗");
              System.out.println("val=" + val);
      //        size:获取元素个数
              System.out.println("k-v=" + map.size());
      //        isEmpty:判断个数是否为0
              System.out.println(map.isEmpty());//F
      //        clear:清除k-v
              //map.clear();
              System.out.println("map=" + map);
      //        containsKey:查找键是否存在
              System.out.println("结果=" + map.containsKey("hsp"));//T
          }
      }
      
      class Book {
          private String name;
          private int num;
      
          public Book(String name, int num) {
              this.name = name;
              this.num = num;
          }
      }
      
  • 遍历方式:
    1. 先取出 所有的Key , 通过Key 取出对应的Value

    2. 把所有的values取出

    3. 通过 EntrySet 来获取 k-v

      public class MapFor {
          public static void main(String[] args) {
      
              Map map = new HashMap();
              map.put("邓超", "孙俪");
              map.put("王宝强", "马蓉");
              map.put("宋喆", "马蓉");
              map.put("刘令博", null);
              map.put(null, "刘亦菲");
              map.put("鹿晗", "关晓彤");
      
              //第一组: 先取出 所有的Key , 通过Key 取出对应的Value
              Set keyset = map.keySet();
              //(1) 增强for
              System.out.println("---第一种方式-------");
              for (Object key : keyset) {
                  System.out.println(key + "-" + map.get(key));
              }
              //(2) 迭代器
              System.out.println("----第二种方式--------");
              Iterator iterator = keyset.iterator();
              while (iterator.hasNext()) {
                  Object key =  iterator.next();
                  System.out.println(key + "-" + map.get(key));
              }
      
              //第二组: 把所有的values取出
              Collection values = map.values();
              //这里可以使用所有的Collections使用的遍历方法
              //(1) 增强for
              System.out.println("---取出所有的value 增强for----");
              for (Object value : values) {
                  System.out.println(value);
              }
              //(2) 迭代器
              System.out.println("---取出所有的value 迭代器----");
              Iterator iterator2 = values.iterator();
              while (iterator2.hasNext()) {
                  Object value =  iterator2.next();
                  System.out.println(value);
      
              }
      
              //第三组: 通过EntrySet 来获取 k-v
              Set entrySet = map.entrySet();// EntrySet<Map.Entry<K,V>>
              //(1) 增强for
              System.out.println("----使用EntrySet 的 for增强(第3种)----");
              for (Object entry : entrySet) {
                  //将entry 转成 Map.Entry
                  Map.Entry m = (Map.Entry) entry;
                  System.out.println(m.getKey() + "-" + m.getValue());
              }
              //(2) 迭代器
              System.out.println("----使用EntrySet 的 迭代器(第4种)----");
              Iterator iterator3 = entrySet.iterator();
              while (iterator3.hasNext()) {
                  Object entry =  iterator3.next();
                  //System.out.println(next.getClass());//HashMap$Node -实现-> Map.Entry (getKey,getValue)
                  //向下转型 Map.Entry
                  Map.Entry m = (Map.Entry) entry;
                  System.out.println(m.getKey() + "-" + m.getValue());
              }
          }
      }
      

12.3.1 HashMap⭐️

  • 注意事项和细节:
    1. HashMap是Map 接口使用频率最高的实现类
    2. Hashap 是以 key-val 对的方式来存储数据(HashMap$Node类型)
    3. key 不能重复,但是值可以重复,允许使用null和null值
    4. 如果添加相同的key,则会覆盖原来的key-val ,等同于修改(key不会替换,val会替换)
    5. 与HashSet一样,不保证映射的顺序,因为底层是以hash表的方式来存储的(jdk8的hashMap 底层 数组+链表+红黑树)
    6. HashMap没有实现同步,因此是线程不安全的,方法没有做同步互斥的操作,没有synchronized
  • 底层机制和源码剖析:
    1. 扩容机制和Hashset相同

    2. HashMap底层维护了Node类型的数组table,默认为null

    3. 当创建对象时,将加载因子(loadfactor)初始化为0.75

    4. 当添加key-val时,通过key的哈希值得到在table的索引。然后判断该索引处是否有元素,如果没有元素直接添加。如果该索引处有元素,继续判断该元素的key是否和准备加入的key相等,如果相等,则直接替换val:如果不相等需要判断是树结构还是链表结构,做出相应处理。如果添加时发现容量不够,则需要扩容

    5. 第1次添加,则需要扩容table容量为16,临界值(threshold)为12

    6. 以后再扩容,则需要扩容table容量为原来的2倍,临界值为原来的2倍,即24,依次类推

    7. 在Java8中,如果一条链表的元素个数超过 TREEIFY THRESHOLD(默认是8),并且
      table的大小>=MIN TREEIFY CAPACITY(默认64),就会进行树化(红黑树)

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hEqMazbq-1692078080352)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-29%2016.13.53.jpg)]

      public class HashMapSource1 {
          public static void main(String[] args) {
              HashMap map = new HashMap();
              map.put("java", 10);//ok
              map.put("php", 10);//ok
              map.put("java", 20);//替换value
      
              System.out.println("map=" + map);//
      
              /*老韩解读HashMap的源码+图解
              1. 执行构造器 new HashMap()
                 初始化加载因子 loadfactor = 0.75
                 HashMap$Node[] table = null
              2. 执行put 调用 hash方法,计算 key的 hash值 (h = key.hashCode()) ^ (h >>> 16)
                  public V put(K key, V value) {//K = "java" value = 10
                      return putVal(hash(key), key, value, false, true);
                  }
              3. 执行 putVal
               final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                         boolean evict) {
                      Node<K,V>[] tab; Node<K,V> p; int n, i;//辅助变量
                      //如果底层的table 数组为null, 或者 length =0 , 就扩容到16
                      if ((tab = table) == null || (n = tab.length) == 0)
                          n = (tab = resize()).length;
                      //取出hash值对应的table的索引位置的Node, 如果为null, 就直接把加入的k-v
                      //, 创建成一个 Node ,加入该位置即可
                      if ((p = tab[i = (n - 1) & hash]) == null)
                          tab[i] = newNode(hash, key, value, null);
                      else {
                          Node<K,V> e; K k;//辅助变量
                      // 如果table的索引位置的key的hash相同和新的key的hash值相同,
                       // 并 满足(table现有的结点的key和准备添加的key是同一个对象  || equals返回真)
                       // 就认为不能加入新的k-v
                          if (p.hash == hash &&
                              ((k = p.key) == key || (key != null && key.equals(k))))
                              e = p;
                          else if (p instanceof TreeNode)//如果当前的table的已有的Node 是红黑树,就按照红黑树的方式处理
                              e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
                          else {
                              //如果找到的结点,后面是链表,就循环比较
                              for (int binCount = 0; ; ++binCount) {//死循环
                                  if ((e = p.next) == null) {//如果整个链表,没有和他相同,就加到该链表的最后
                                      p.next = newNode(hash, key, value, null);
                                      //加入后,判断当前链表的个数,是否已经到8个,到8个,后
                                      //就调用 treeifyBin 方法进行红黑树的转换
                                      if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                                          treeifyBin(tab, hash);
                                      break;
                                  }
                                  if (e.hash == hash && //如果在循环比较过程中,发现有相同,就break,就只是替换value
                                      ((k = e.key) == key || (key != null && key.equals(k))))
                                      break;
                                  p = e;
                              }
                          }
                          if (e != null) { // existing mapping for key
                              V oldValue = e.value;
                              if (!onlyIfAbsent || oldValue == null)
                                  e.value = value; //替换,key对应value
                              afterNodeAccess(e);
                              return oldValue;
                          }
                      }
                      ++modCount;//每增加一个Node ,就size++
                      if (++size > threshold[12-24-48])//如size > 临界值,就扩容
                          resize();
                      afterNodeInsertion(evict);
                      return null;
                  }
      
                    5. 关于树化(转成红黑树)
                    //如果table 为null ,或者大小还没有到 64,暂时不树化,而是进行扩容.
                    //否则才会真正的树化 -> 剪枝
                    final void treeifyBin(Node<K,V>[] tab, int hash) {
                      int n, index; Node<K,V> e;
                      if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
                          resize();
                  }
               */
          }
      }
      

12.3.2 Hashtable

  • 注意事项和细节:
    1. 存放的元素是键值对:即K-V
    2. hashtable的键和值都不能为null, 否则会抛出NulPointerException
    3. hashTable 使用方法基本上和HashMap一样
    4. hashTable 是线程安全的(synchronized),hashMap 是线程不安全的
  • 底层机制:
    1. 底层有数组 Hashtable$Entry[] 初始化大小为 11
    2. 临界值 threshold 8 = 11 * 0.75
    3. 扩容: 按照自己的扩容机制来进行即可
    4. 执行方法 addEntry(hash, key, value, index); 添加K-V 封装到Entry
    5. 当 if (count >= threshold) 满足时,就进行扩容
    6. 按照 int newCapacity = (oldCapacity << 1) + 1;的大小扩容
  • Hashtable和HashMapd:

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gcqWjs0N-1692078080352)(https://xingqiu-tuchuang-1256524210.cos.ap-shanghai.myqcloud.com/3833/%E6%88%AA%E5%B1%8F2022-05-29%2016.14.26.jpg)]

12.3.3 LinkedHashMap

12.3.4. TreeMap

  • 底层机制和源码剖析:
    1. 若按照compare方法比较key相同则无法加入value值

      public class TreeMap_ {
          public static void main(String[] args) {
      
              //使用默认的构造器,创建TreeMap, 是字母排序
              /*
                  要求:按照传入的 k(String) 的大小进行排序
               */
      //        TreeMap treeMap = new TreeMap();
              TreeMap treeMap = new TreeMap(new Comparator() {
                  @Override
                  public int compare(Object o1, Object o2) {
                      //按照传入的 k(String) 的大小进行排序
                      //按照K(String) 的长度大小排序
                      //return ((String) o2).compareTo((String) o1);
                      return ((String) o2).length() - ((String) o1).length();
                  }
              });
              treeMap.put("jack", "杰克");
              treeMap.put("tom", "汤姆");
              treeMap.put("kristina", "克瑞斯提诺");
              treeMap.put("smith", "斯密斯");
              treeMap.put("hsp", "韩顺平");//加入不了
      
              System.out.println("treemap=" + treeMap);
      
              /*
                  解读源码:
                  1. 构造器. 把传入的实现了 Comparator接口的匿名内部类(对象),传给给TreeMap的comparator
                   public TreeMap(Comparator<? super K> comparator) {
                      this.comparator = comparator;
                  }
                  2. 调用put方法
                  2.1 第一次添加, 把k-v 封装到 Entry对象,放入root
                  Entry<K,V> t = root;
                  if (t == null) {
                      compare(key, key); // type (and possibly null) check
      
                      root = new Entry<>(key, value, null);
                      size = 1;
                      modCount++;
                      return null;
                  }
                  2.2 以后添加
                  Comparator<? super K> cpr = comparator;
                  if (cpr != null) {
                      do { //遍历所有的key , 给当前key找到适当位置
                          parent = t;
                          cmp = cpr.compare(key, t.key);//动态绑定到我们的匿名内部类的compare
                          if (cmp < 0)
                              t = t.left;
                          else if (cmp > 0)
                              t = t.right;
                          else  //如果遍历过程中,发现准备添加Key 和当前已有的Key 相等,就不添加
                              return t.setValue(value);
                      } while (t != null);
                  }
               */
          }
      }
      

12.3.5. Properties

  • 注意事项和细节:
    1. Properties类继承Hashtable类,实现了Map接口,也是使用一种简直对的形式保存数据

    2. 使用特点和Hashtable类似

    3. Properties 还可以用于 从xxx.properties 文件中,加载数据到Properties类对象井进行读取和修改

    4. xxx.properties 文件通常作为配置文件

      public class Properties_ {
          public static void main(String[] args) {
            
              //1. Properties 继承  Hashtable
              //2. 可以通过 k-v 存放数据,当然key 和 value 不能为 null
              //增加
              Properties properties = new Properties();
              //properties.put(null, "abc");//抛出 空指针异常
              //properties.put("abc", null); //抛出 空指针异常
              properties.put("john", 100);//k-v
              properties.put("lucy", 100);
              properties.put("lic", 100);
              properties.put("lic", 88);//如果有相同的key , value被替换
      
              System.out.println("properties=" + properties);
      
              //通过k 获取对应值
              System.out.println(properties.get("lic"));//88
      
              //删除
              properties.remove("lic");
              System.out.println("properties=" + properties);
      
              //修改
              properties.put("john", "约翰");
              System.out.println("properties=" + properties);
          }
      }
      

12.4 Collections

  • 基本介绍:
    • Collections 是一个操作 Set.List 和 Map 等集合的工具类
    • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作
  • 常用方法:
    • 排序操作:

      • reverse (List):反转List中元素顺序
      • shuffle(List):对 List 集合元素进行随机排序
      • sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
      • sort(List, Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
      • swap (List, int,int):将指定 list 集合中的 i处元素和j处元素进行交换
    • 查找替换:

      • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素

      • Object max(Collection, Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素

      • Object min(Collection)

      • Object min(Collection, Comparator)

      • int frequency(Collection, Object):返回指定集合中指定元素的出现次数

      • void copy(List dest, List src):将src中的内容复制到dest中

      • boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换 List 对象的所有旧值

        public class Collections_ {
            public static void main(String[] args) {
        
                //创建ArrayList 集合,用于测试.
                List list = new ArrayList();
                list.add("tom");
                list.add("smith");
                list.add("king");
                list.add("milan");
                list.add("tom");
        
        //        reverse(List):反转 List 中元素的顺序
                Collections.reverse(list);
                System.out.println("list=" + list);
        //        shuffle(List):对 List 集合元素进行随机排序
        //        for (int i = 0; i < 5; i++) {
        //            Collections.shuffle(list);
        //            System.out.println("list=" + list);
        //        }
        
        //        sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
                Collections.sort(list);
                System.out.println("自然排序后");
                System.out.println("list=" + list);
        //        sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
                //我们希望按照 字符串的长度大小排序
                Collections.sort(list, new Comparator() {
                    @Override
                    public int compare(Object o1, Object o2) {
                        //可以加入校验代码.
                        return ((String) o2).length() - ((String) o1).length();
                    }
                });
                System.out.println("字符串长度大小排序=" + list);
        //        swap(List,int, int):将指定 list 集合中的 i 处元素和 j 处元素进行交换
        
                //比如
                Collections.swap(list, 0, 1);
                System.out.println("交换后的情况");
                System.out.println("list=" + list);
        
                //Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
                System.out.println("自然顺序最大元素=" + Collections.max(list));
                //Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
                //比如,我们要返回长度最大的元素
                Object maxObject = Collections.max(list, new Comparator() {
                    @Override
                    public int compare(Object o1, Object o2) {
                        return ((String)o1).length() - ((String)o2).length();
                    }
                });
                System.out.println("长度最大的元素=" + maxObject);
        
        
                //Object min(Collection)
                //Object min(Collection,Comparator)
                //上面的两个方法,参考max即可
        
                //int frequency(Collection,Object):返回指定集合中指定元素的出现次数
                System.out.println("tom出现的次数=" + Collections.frequency(list, "tom"));
        
                //void copy(List dest,List src):将src中的内容复制到dest中
        
                ArrayList dest = new ArrayList();
                //为了完成一个完整拷贝,我们需要先给dest 赋值,大小和list.size()一样
                for(int i = 0; i < list.size(); i++) {
                    dest.add("");
                }
                //拷贝
                Collections.copy(dest, list);
                System.out.println("dest=" + dest);
        
                //boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
                //如果list中,有tom 就替换成 汤姆
                Collections.replaceAll(list, "tom", "汤姆");
                System.out.println("list替换后=" + list);
            }
        }
        

12.5 总结⭐️⭐️

  • 选择集合:
    • 先判断存储的类型(一组对象[单列]或一组键值对[双列])

    • 一组对象[单列]: Collection接口

      • 允许重复:List

        增删多:LinkedList [底层维护双向链表]

        改查多:ArrayList [底层維护 Object类型的可变数组]

      • 不允许重复:Set

        无序:HashSet [底层是HashMap,维护了一个哈希表,即(数组+链表+红黑树)]

        排序:Treeset []

        插入和取出顺序一致:LinkedHashSet [底层维护数组+双向链表]

    • 一组键[值对双列]:Map

      • 键无序:HashMap [底层是:哈希表 jdk7:数组+链表,jdk8:数组+链表+红黑树]
      • 键排序:TreeMap []
      • 键插入和取出顺序一致:LinkedHashMap
      • 读取文件 Propertie

13 泛型

13.1 泛型

  • 基本介绍:
    • 泛型又称参数化类型(接收数据类型的数据类型),是Jdk5.0出现的新特性,解决数据类型的安全性问题
    • 在类声明或实例化时只要指定好需要的具体的类型即可
    • Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮
  • 基本语法:
    interface 接口 <T>{}
    class<K,V>{}
    
    在类名后指定类型参数的值(类型):
      List<String> strList = new ArrayList<String>();
      lterator<Customer> iterator = customers.iterator();
    
  • 注意事项和细节:
    1. 泛型只能是引用类型
    2. 在给泛型指定具体类型后,可以传入该类型或者其子类类型
    3. List list3 = new ArrayList():默认给它的 泛型是[<E> E就是 Object]
  • 例:
    public class GenericExercise02 {
        public static void main(String[] args) {
            ArrayList<Employee> employees = new ArrayList<Employee>();
            employees.add(new Employee("jack", 2000, new MyDate(1, 1, 1)));
            employees.add(new Employee("tom", 2000, new MyDate(6, 1, 1)));
            employees.add(new Employee("tom", 3000, new MyDate(3, 1, 1)));
            employees.add(new Employee("jack", 2000, new MyDate(1, 1, 3)));
    
            employees.sort(new Comparator<Employee>() {//传入匿名内部类接口重写compare方法
                @Override
                public int compare(Employee o1, Employee o2) {
                    if (!(o1 instanceof Employee) && (o2 instanceof Employee)) {
                        System.out.println("类型不正确");
                        return 0;
                    }
                    return o1.getBirthday().compareTo(o2.getBirthday());//在MyDate内重写compareTo方法
                }
            });
    
            for (Employee employee : employees) {
                System.out.println(employee);
            }
        }
    }
    
    class Employee {
        private String name;
        private int sal;
        private MyDate birthday;
    
        public Employee(String name, int sal, MyDate birthday) {
            this.name = name;
            this.sal = sal;
            this.birthday = birthday;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getSal() {
            return sal;
        }
    
        public void setSal(int sal) {
            this.sal = sal;
        }
    
        public MyDate getBirthday() {
            return birthday;
        }
    
        public void setBirthday(MyDate birthday) {
            this.birthday = birthday;
        }
    
        @Override
        public String toString() {
            return "Employee{" +
                    "name='" + name + '\'' +
                    ", sal=" + sal +
                    ", birthday=" + birthday +
                    '}';
        }
    }
    
    class MyDate implements Comparable<MyDate> {
    
        private int year;
        private int month;
        private int day;
    
        public MyDate(int year, int month, int day) {
            this.year = year;
            this.month = month;
            this.day = day;
        }
    
        public int getYear() {
            return year;
        }
    
        public void setYear(int year) {
            this.year = year;
        }
    
        public int getMonth() {
            return month;
        }
    
        public void setMonth(int month) {
            this.month = month;
        }
    
        public int getDay() {
            return day;
        }
    
        public void setDay(int day) {
            this.day = day;
        }
    
        @Override
        public String toString() {
            return "MyDate{" +
                    "year=" + year +
                    ", month=" + month +
                    ", day=" + day +
                    '}';
        }
    
        @Override
        public int compareTo(MyDate o) {
            int yearMinus = year - o.year;
            if (yearMinus != 0) {
                return yearMinus;
            }
            int monthMinus = month - o.month;
            if (monthMinus != 0) {
                return monthMinus;
            }
            return day - o.day;
        }
    }
    

13.2 自定义泛型

13.2.1泛型类

  • 基本语法:
    class 类名<T,R...>{
      成员;
    }
    
  • 注意事项和细节:
    1. 普通成员可以使用泛型(属性、方法)

    2. 使用泛型的数组,不能初始化

    3. 静态方法中不能使用类的泛型

    4. 泛型类的类型,是在创建对象时确定的(因为创建对象时,需要指定确定类型)

    5. 如果在创建对象时,没有指定类型,默认为Object

      public class CustomGeneric_ {
          public static void main(String[] args) {
      
              //T=Double, R=String, M=Integer
              Tiger<Double,String,Integer> g = new Tiger<>("john");
              g.setT(10.9); //OK
              //g.setT("yy"); //错误,类型不对
              System.out.println(g);
              Tiger g2 = new Tiger("john~~");//OK T=Object R=Object M=Object
              g2.setT("yy"); //OK ,因为 T=Object "yy"=String 是Object子类
              System.out.println("g2=" + g2);
      
          }
      }
      
      //1. Tiger 后面泛型,所以我们把 Tiger 就称为自定义泛型类
      //2, T, R, M 泛型的标识符, 一般是单个大写字母
      //3. 泛型标识符可以有多个.
      //4. 普通成员可以使用泛型 (属性、方法)
      //5. 使用泛型的数组,不能初始化
      //6. 静态方法中不能使用类的泛型
      class Tiger<T, R, M> {
          String name;
          R r; //属性使用到泛型
          M m;
          T t;
          //因为数组在new 不能确定T的类型,就无法在内存开空间
          T[] ts;
      
          public Tiger(String name) {
              this.name = name;
          }
      
          public Tiger(R r, M m, T t) {//构造器使用泛型
      
              this.r = r;
              this.m = m;
              this.t = t;
          }
      
          public Tiger(String name, R r, M m, T t) {//构造器使用泛型
              this.name = name;
              this.r = r;
              this.m = m;
              this.t = t;
          }
      
          //因为静态是和类相关的,在类加载时,对象还没有创建
          //所以,如果静态方法和静态属性使用了泛型,JVM就无法完成初始化
      //    static R r2;
      //    public static void m1(M m) {
      //
      //    }
      
          //方法使用泛型
      
          public String getName() {
              return name;
          }
      
          public void setName(String name) {
              this.name = name;
          }
      
          public R getR() {
              return r;
          }
      
          public void setR(R r) {//方法使用到泛型
              this.r = r;
          }
      
          public M getM() {//返回类型可以使用泛型.
              return m;
          }
      
          public void setM(M m) {
              this.m = m;
          }
      
          public T getT() {
              return t;
          }
      
          public void setT(T t) {
              this.t = t;
          }
      
          @Override
          public String toString() {
              return "Tiger{" +
                      "name='" + name + '\'' +
                      ", r=" + r +
                      ", m=" + m +
                      ", t=" + t +
                      ", ts=" + Arrays.toString(ts) +
                      '}';
          }
      }
      

13.2.2泛型接口

  • 基本语法:
    interface 接口名<T,R...>{}
    
  • 注意事项和细节:
    1. 静态成员不能使用泛型

    2. 泛型接口的类型,在继承接口或者实现接口时确定

    3. 没有指定类型,默认为Object

      public class CustomInterfaceGeneric {
          public static void main(String[] args) {
      
          }
      }
      
      /**
       *  泛型接口使用的说明
       *  1. 接口中,静态成员也不能使用泛型
       *  2. 泛型接口的类型, 在继承接口或者实现接口时确定
       *  3. 没有指定类型,默认为Object
       */
      
      //在继承接口 指定泛型接口的类型
      interface IA extends IUsb<String, Double> {
      
      }
      //当我们去实现IA接口时,因为IA在继承IUsu 接口时,指定了U 为String R为Double
      //,在实现IUsu接口的方法时,使用String替换U, 是Double替换R
      class AA implements IA {
      
          @Override
          public Double get(String s) {
              return null;
          }
          @Override
          public void hi(Double aDouble) {
      
          }
          @Override
          public void run(Double r1, Double r2, String u1, String u2) {
      
          }
      }
      
      //实现接口时,直接指定泛型接口的类型
      //给U 指定Integer 给 R 指定了 Float
      //所以,当我们实现IUsb方法时,会使用Integer替换U, 使用Float替换R
      class BB implements IUsb<Integer, Float> {
      
          @Override
          public Float get(Integer integer) {
              return null;
          }
      
          @Override
          public void hi(Float aFloat) {
      
          }
      
          @Override
          public void run(Float r1, Float r2, Integer u1, Integer u2) {
      
          }
      }
      //没有指定类型,默认为Object
      //建议直接写成 IUsb<Object,Object>
      class CC implements IUsb { //等价 class CC implements IUsb<Object,Object> {
          @Override
          public Object get(Object o) {
              return null;
          }
          @Override
          public void hi(Object o) {
          }
          @Override
          public void run(Object r1, Object r2, Object u1, Object u2) {
      
          }
      }
      
      interface IUsb<U, R> {
      
          int n = 10;
          //U name; 不能这样使用
      
          //普通方法中,可以使用接口泛型
          R get(U u);
      
          void hi(R r);
      
          void run(R r1, R r2, U u1, U u2);
      
          //在jdk8 中,可以在接口中,使用默认方法, 也是可以使用泛型
          default R method(U u) {
              return null;
          }
      }
      

13.2.3泛型方法

  • 基本语法:
    修饰符<T,R...>返回类型 方法名(参数列表){}
    
  • 注意事项和细节:
    1. 泛型方法,可以定义在普通类中,也可以定义在泛型类中

    2. 当泛型方法被调用时,类型会确定

    3. public void eat(E e) {},
      修饰符后没有<T,R…> eat
      方法不是泛型方法,而是使用了泛型

      public class CustomMethodGeneric {
          public static void main(String[] args) {
              Car car = new Car();
              car.fly("宝马", 100);//当调用方法时,传入参数,编译器,就会确定类型
              System.out.println("=======");
              car.fly(300, 100.1);//当调用方法时,传入参数,编译器,就会确定类型
      
              //测试
              //T->String, R-> ArrayList
              Fish<String, ArrayList> fish = new Fish<>();
              fish.hello(new ArrayList(), 11.3f);
          }
      }
      
      //泛型方法,可以定义在普通类中, 也可以定义在泛型类中
      class Car {//普通类
      
          public void run() {//普通方法
          }
          //说明 泛型方法
          //1. <T,R> 就是泛型
          //2. 是提供给 fly使用的
          public <T, R> void fly(T t, R r) {//泛型方法
              System.out.println(t.getClass());//String
              System.out.println(r.getClass());//Integer
          }
      }
      
      class Fish<T, R> {//泛型类
          public void run() {//普通方法
          }
          public<U,M> void eat(U u, M m) {//泛型方法
      
          }
          //说明
          //1. 下面hi方法不是泛型方法
          //2. 是hi方法使用了类声明的 泛型
          public void hi(T t) {
          }
          //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
          public<K> void hello(R r, K k) {
              System.out.println(r.getClass());//ArrayList
              System.out.println(k.getClass());//Float
          }
      }
      

13.3 泛型继承和通配符

  • 注意事项和细节:
    1. 泛型不具备继承性

    2. <?>:支持任意泛型类型
    3. <? extends A>:支持A类以及A类的子类,规定了泛型的上限
    4. <? super A>:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
      public class GenericExtends {
          public static void main(String[] args) {
      
              Object o = new String("xx");
      
              //泛型没有继承性
              //List<Object> list = new ArrayList<String>();
      
              //举例说明下面三个方法的使用
              List<Object> list1 = new ArrayList<>();
              List<String> list2 = new ArrayList<>();
              List<AA> list3 = new ArrayList<>();
              List<BB> list4 = new ArrayList<>();
              List<CC> list5 = new ArrayList<>();
      
              //如果是 List<?> c ,可以接受任意的泛型类型
              printCollection1(list1);
              printCollection1(list2);
              printCollection1(list3);
              printCollection1(list4);
              printCollection1(list5);
      
              //List<? extends AA> c: 表示 上限,可以接受 AA或者AA子类
      //        printCollection2(list1);//×
      //        printCollection2(list2);//×
              printCollection2(list3);//√
              printCollection2(list4);//√
              printCollection2(list5);//√
      
              //List<? super AA> c: 支持AA类以及AA类的父类,不限于直接父类
              printCollection3(list1);//√
              //printCollection3(list2);//×
              printCollection3(list3);//√
              //printCollection3(list4);//×
              //printCollection3(list5);//×
      
      
              //冒泡排序
      
              //插入排序
      
              //....
      
          }
          // ? extends AA 表示 上限,可以接受 AA或者AA子类
          public static void printCollection2(List<? extends AA> c) {
              for (Object object : c) {
                  System.out.println(object);
              }
          }
      
          //说明: List<?> 表示 任意的泛型类型都可以接受
          public static void printCollection1(List<?> c) {
              for (Object object : c) { // 通配符,取出时,就是Object
                  System.out.println(object);
              }
          }
      
      
          // ? super 子类类名AA:支持AA类以及AA类的父类,不限于直接父类,
          //规定了泛型的下限
          public static void printCollection3(List<? super AA> c) {
              for (Object object : c) {
                  System.out.println(object);
              }
          }
      }
      
      class AA {
      }
      
      class BB extends AA {
      }
      
      class CC extends BB {
      }
      
  • 例:
    public class HomeWork01 {
        public static void main(String[] args) {
    
        }
        @Test
        public void testList() {
             DAO<User> dao = new DAO<User>();
             dao.save("001",new User(1,10,"jack"));
             dao.save("002",new User(2,11,"tom"));
             dao.save("003",new User(3,12,"mike"));
            List<User> list = dao.list();
            System.out.println(list);
    
            dao.update("002",new User(2,14,"faker"));
            List<User> list1 = dao.list();
            System.out.println(list1);
    
            dao.delete("001");
            List<User> list2 = dao.list();
            System.out.println(list2);
        }
    }
    
    public class DAO<T> {//泛型类
        private Map<String, T> map = new HashMap<>();
    
        public T get(String id) {
            return map.get(id);
        }
        public void update(String id,T entity) {
            map.put(id, entity);
        }
        //返回 map 中存放的所有 T 对象
        //遍历map [k-v],将map的 所有value(T entity),封装到ArrayList返回即可
        public List<T> list() {
            //创建 Arraylist
            List<T> list = new ArrayList<>();
    
            //遍历map
            Set<String> keySet = map.keySet();
            for (String key : keySet) {
                //map.get(key) 返回就是 User对象->ArrayList
                list.add(map.get(key));//也可以直接使用本类的 get(String id)
            }
    
            return list;
        }
        public void delete(String id) {
            map.remove(id);
        }
        public void save(String id,T entity) {//把entity保存到map
            map.put(id, entity);
        }
    }
    
    public class User {
        private int id;
        private int age;
        private String name;
    
        public User(int id, int age, String name) {
            this.id = id;
            this.age = age;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", age=" + age +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JLAU Veritas

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值