JavaSE笔记总结02(Collection集合List、Set、Map)

7 常用API

7.1 Object类

  1. toString方法
  • 概念
    常用方法:
        1.public String toString():
        --默认是返回当前对象在堆内存的地址信息
        -- 默认是地址信息格式:类的全限名@内存地址
        -- 直接输出对象名称,默认会自动调用toString
        --开发中直接输出对象,默认输出对象的地址实际是毫无意义的
          开发中输出对象变量,更多的时候是希望看到对象的内容数据,而不是对象的地址信息
            所以父类tostring方法存在的意义就是为了被子类重写,以便输出对象的内容信息
  • 案例
public class ObjectToStringDemo01 {
    public static void main(String[] args) {
        Student xm = new Student("xiaoming",18,'女');
        //直接调用toString方法返回的是对象在内存重点的地址
        System.out.println(xm.toString());//调的是子类的重写的toString方法
        //直接输出对象名称,默认会自动调用toString
        System.out.println(xm);
    }
}
  1. equals方法
  • 概念
    常用方法:
        2.public boolean equals:
            --默认比较两个对象的地址是否相同。相同返回true
            --比较两个对象地址是否完全相同完全可以用“==”替代equals
                所以equals存在的意义是为了子类被重写,以便程序员自己定制,还是希望程序员自己比较内容
            --对应需求;
                只要两个对象的内容一样,我们就认为他们是相等的
  • 案例
public class ObjectEqualDemo02 {
    public static void main(String[] args) {
        Student xiaohong = new Student("小红", 18,'女');
        Student xiaoyan = new Student("小红", 18,'女');
        //System.out.println(xiaohong==xiaoyan);
        System.out.println(xiaohong.equals(xiaoyan));//两个对象肯定地址不同
    }
}

7.2 Objects类

  1. equals()和isNull()
  • 概念
    Object的方法:
        1.public static boolean equals(Object a,Object b)
            --比较两个对象的地址是否一样
            --底层有进行非空判断,从而可以避免空指针异常。更安全!
            底层equals源码:
            public static boolean equals(Object a, Object b) {
            return (a == b) || (a != null && a.equals(b));
            }
            如果是a==b是同一个对象,直接返回true,如果不是同一个对象,进行下面的判断,
            如果a不等于空是错的【即等于空】,直接返回错了,如果不为空,继续下面的,才会进行equals的判断
  • 代码
public class ObjectDemo {
    public static void main(String[] args) {
//        Student s1 = new Student();
//        Student s2 = new Student();
//        System.out.println(s1.equals(s2));
//        System.out.println(Objects.equals(s1, s2));//打印结果是false,因为两个对象内存地址肯定不同啊

        Student s1 = null;
        Student s2 = new Student();
        System.out.println(Objects.equals(s1, s2));
        //System.out.println(s1.equals(s2));//报错啦~~,那空的对象去调方法肯定会出现空指针异常

        //询问s1是否为null,如果是null,返回true
        System.out.println(Objects.isNull(s1));
    }
}

7.3 Date日期类

  1. Date类构造器和getTime()
  • 概念
    Date类:
        包:java.util.Date
        构造器:
            --public Date():创建当前系统的此刻日期时间对象
            --public Date(long time):
        方法:
            --public long getTime():返回197011日起的毫秒数
  • 代码

Date()无参构造器案例

public class DataDemo01 {
    public static void main(String[] args) {
        //a.创建一个日期对象代表了系统此刻日期时间对象
        Date d = new Date();
        System.out.println(d);//默认是打印出地址的,但是没写toString也能打印内容,说明重写了toString方法

        //b.拿当前日期对象的时间毫秒值
        long time = d.getTime();
        System.out.println(time);//毫秒值
    }
}

时间毫秒值getTime()做计算的案例

public class DateDemo02 {
    public static void main(String[] args) {
        //1.拿到此刻日期时间对象毫秒值
        long startTime = new Date().getTime();
        for (int i = 0; i < 1000000; i++) {
            System.out.println("输出:"+i);
        }
        //2.拿到此刻日期时间对象毫秒值,差值就是代码执行的时间
        long endTime = new Date().getTime();
        System.out.println((endTime-startTime)/1000.0+"s");//看不到小数呀,1000后面加.0
    }
}

Date(long time)有参构造器案例

目的是把毫秒值转换成日期对象

public class DateDemo03 {
    public static void main(String[] args) {
        //需求:问121s 以后的时间是多少
        //1.拿到此刻日期对象
        Date d = new Date();
        System.out.println(d);

        //2.拿到此刻日期对象的时间毫秒值  往后走121s
        long time = d.getTime()+121*1000;

        //3.把时间毫秒值转换成日期对象
        Date d1 = new Date(time);
        System.out.println(d1);
    }
}
  1. SimpleDateFormat类
  • 概念
    SimpleDateFormat简单日期格式化类:
        包:java.text.SimpleDateFormat\
        构造器:SimpleDateFormat(String pattern):
                指定时间的格式创建简单日期格式化对象
        方法:
            --public String format(Date date):可以把日期对象格式化成我们喜欢的时间形式,返回的是字符串
            --public String format(object time):可以把时间毫秒值格式化成我们喜欢的时间形式
            --public Date parse(String date) throws ParseException:把字符串解析成日期对象
  • 代码

构造器SimpleDateFormat(String pattern)和format(Date date)

public class SimpleDateFormatDemo01 {
    public static void main(String[] args) {
        //需求:把此刻日期对象格式化成我们喜欢的形式
        //1.得到此刻日期对象
        Date d = new Date();
        System.out.println(d);

        //2.创建一个简单日期格式化对象负责格式化时间对象
        //注意:参数是时间的格式,可以参照api帮助文档
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");

        //3.开始调用方法格式化时间得到格式化的字符串时间形式
        String rs = sdf.format(d);
        System.out.println(rs);
    }
}

format(object time)

public class SimpleDateFormatDemo02 {
    public static void main(String[] args) {
        //需求:问121s后的时间是多少
        //a.得到此刻日期时间
        Date date = new Date();
        System.out.println(date);

        //b.得到当前时间的时间毫秒值
        long time = date.getTime();
        time+=121*1000;



        //c或者把毫秒值转成日期对象,再把日期对象格式化\
        Date date1 = new Date(time);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
        System.out.println(sdf.format(date1));


        //d.格式化时间毫秒值
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
        System.out.println(sdf1.format(time));
    }
}

parse(String date)以及综合案例

public class SimpleDateFormatDemo03 {
    public static void main(String[] args) throws ParseException {
        //面试题:请写出”2022-11-04 09:30:30“往后1天15小时,30分29秒后的时间是多少?
        //1.定义一个字符串时间
        String date = "2022-11-04 09:30:30";
        //2.把字符串的时间解析成Date日期对象
            //a.创建一个简单日期格式化对象负责解析字符串的时间成为日期对象,注意!参数必须与被解析的时间的格式完全一致,不然报错呢!
        SimpleDateFormat sfd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            //b.开始解析成日期对象
        Date newDate = sfd.parse(date);
        //3.得到日期对象的时间毫秒值+往后走1天15小时,30分29秒
        long time = (long) (newDate.getTime()+(24L*60*60+15*60*60+0.5*60*60+29) * 1000);//防止int计算的时候,范围溢出,因此24后面加一个L安全
        //4.把时间毫秒值转换成喜欢的字符串的时间形式
        System.out.println(sfd.format(time));
    }
}

7.4 Calender日历类

  1. 概念
    Calender日历类创建日历对象的语法(是一个抽象类):
        Calender rightNow = Calender.getInstance();//单例写法,用方法new出来
    Calender的方法:
        1.public static Calender getInstance():返回一个日历类对象
        2.public int get(int field):取日期中的某个字段信息
        3.public int set(int field,int value):修改日历的某个字段信息
        4.
        5.public final Date getTime():拿到此刻日历对象
  1. 代码
public class CalenderDemo01 {
    public static void main(String[] args) {
        //1,通过调用日历类的静态方法getInstance
        Calendar rightNow = Calendar.getInstance();
        System.out.println(rightNow);//没有打印地址,打的是内容,说明在日历类中重写了toString方法

        //2.获取年
        int year = rightNow.get(Calendar.YEAR);
        System.out.println(year);

        int month = rightNow.get(Calendar.MONTH)+1;
        System.out.println(month);
        int hour = rightNow.get(Calendar.HOUR);
        System.out.println(hour);

        //3.一年中的第几天:308
        int days = rightNow.get(Calendar.DAY_OF_YEAR);
        System.out.println(days);

        //4.1修改日历的信息
        //rightNow.set(Calendar.YEAR,2099);
        //System.out.println(rightNow.get(Calendar.YEAR));

        //4.2可以让日历往后走多少天,问367天后的时间
        //让日历的一年中第几天往后走367天
        rightNow.add(Calendar.DAY_OF_YEAR,367);
        rightNow.add(Calendar.HOUR,7);
        long time1 = rightNow.getTimeInMillis();
        SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss EEE a");
        System.out.println(sdf1.format(time1));


        //5.日历可以得到此刻日期对象,不常用
        Date d = rightNow.getTime();
        System.out.println(d);

        //6.此刻时间毫秒值,不常用
        long time = rightNow.getTimeInMillis();
        System.out.println(time);
    }
}

7.5 Math数学类

  1. 概念
    Math用于作数学运算。不能被继承,有被final修饰,构造器私有,不需要创造对象,方法都是静态的,只能用类名调用
    Math类中的方法全部都是静态方法,直接用类名调用即可。
    方法:
            方法名                                             说明
        public static int abs()                         获取参数a的绝对值
        public static double ceil(double a)             向上取整
        public static double floor(double a)            向下去争
        public static double pow(double a,double b)     获取a的b次幂
        public static long round(double a)              四舍五入取整
  1. 代码
public class MathDemo {
    public static void main(String[] args) {
        //1.取绝对值
        System.out.println(Math.abs(10));
        System.out.println(Math.abs(-10.3));
        //2.向上取整数
        System.out.println(Math.ceil(4.000001));
        //3.向下取整数
        System.out.println(Math.floor(3.99999));
        //4.求指数次方
        System.out.println(Math.pow(2, 3));//2的三次方
        //5.四舍五入
        System.out.println(Math.round(4.5));
    }
}

7.6 System系统类

  1. 概念
    System代表当前系统
    静态方法:
        1.public static void exit(int status):终止虚拟机,非0是异常终止
        2.public static long currentTimeMillis():获取当前系统此刻时间毫秒值
        3.可以做数组的拷贝
  1. 代码
public class SystemDemo {
    public static void main(String[] args) {
        System.out.println("程序开始。。。");

        //1.终止当前虚拟机
        //System.exit(0);//0代表正常终止

        System.out.println("程序结束。。。");



        //2.得到系统当前时间毫秒值
        long time = System.currentTimeMillis();
        //格式化出来看看
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        System.out.println(sdf.format(time));


        //3.可以做数组的拷贝(了解)
        int[] arrs1 = new int[]{10,20,30,40};
        int[] arrs2 = new int[6];//[0,0,0,0,0,0]
        //需求:想要把arrs2 = [0,30,40,0,0,0]
        /**
         public static native void arraycopy(Object src,  int  srcPos,Object dest, int destPos,int length);
         参数一:原数组
         参数二:从哪个索引位置开始赋值
         参数三:目标数组
         参数四:目标数组的开始索引
         参数五;复制几个
         */
        System.arraycopy(arrs1,2,arrs2,1,2);
        System.out.println(Arrays.toString(arrs2));//打印数组2的内容
    }
}

7.7 BigDecimal计算浮点型类

  1. 概念
    引入:浮点型运算的时候+ * /可能会出现数据失真(精度问题)
        大数据类可以解决浮点型运算数据失真的问题
    BigDecimal类:
        包:java.math
        创建对象的方法(最好的方式)
            public static BigDecimal valueOf(double val):包装浮点数成为大数据对象

        方法声明
            public BigDecimal add(BigDecimal value)             加法运算
            public BigDecimal substract(BigDecimal value)       减法运算
            public BigDecimal multiply(BigDecimal value)         乘法运算
            public BigDecimal divide(BigDecimal value)          除法运算
            public double doubleValue():BigDecimal转换成double类型
  1. 代码
public class BigDecimalDemo {
    public static void main(String[] args) {
        System.out.println(0.1+0.2);//0.30000000000000004 这些玩意不能给用户看的
        System.out.println(0.09 + 0.01);//0.09999999999999999
        System.out.println(1.0 - 0.32);//0.6799999999999999
        System.out.println(1.015 * 100);//101.49999999999999
        System.out.println(1.301 / 100);//0.013009999999999999

        System.out.println("-----------------------------");
        double a = 0.1;
        double b = 0.2;
        double c = a + b;
        System.out.println(c);

        //用大数据类型:把浮点数转换成大数据对象运算
        BigDecimal a1 = BigDecimal.valueOf(a);
        BigDecimal b1 = BigDecimal.valueOf(b);
        BigDecimal c1 = a1.add(b1);
        BigDecimal c2 = a1.multiply(b1);
        System.out.println(c1);//看下有没有打地址
        System.out.println(c2);

        //实际开发中,结果是还需要继续使用的!!
        //BigDecimal只是解决精度的问题的手段,double才是我们的目的,所以还是要转成double类型继续用
        double rs = c1.doubleValue();
        System.out.println(rs);

    }
}

7.8 包装类

  1. 概念
    引用:
        Java认为一切皆对象。引用数据类型都是对象
        但是在Java中基本数据类型不是对象,是数据的类型形式,这8种数据类型就显得很突兀。
        Java为了一切皆对象的思想统一,把8种基本数据类型转换成对应的类,这个类成为基本数据类型的包装类。

        基本数据类型      包装类()
            byte        Byte
            short       Short
            int         Int
            long        Long

            float       Float
            double      Double
            char        Character
            boolean     Boolean

        自动装箱:可以直接把基本数据类型的值或者变量赋值给包装类
        自动拆箱:可以把包装类的变量直接赋值给基本数据类型
  1. 代码
public class PackageClass {
    public static void main(String[] args) {
        int a = 12;
        Integer a1 = 12;//自动装箱
        Integer a2 = a;//自动装箱

        double b = 99.9;
        Double b1 = 99.9;//自动装箱
        Double b2 = b;//自动装箱

        Integer c = 100;
        Integer d = null;//当对象用   并且引用数据类型可以有null  以后可以用引用类型去接别人的代码
        int c1 = c;//自动拆箱
        //int c2 = null;报错的,基本数据类型不能为null


        System.out.println("==========================");

        Integer it = Integer.valueOf(12);//手动装箱

        Integer it33 = 111;
        int it3 = it33.intValue();//手动拆箱,没必要
    }
}

8 正则表达式

  1. 概念
    正则表达式的作用:
        是一些特殊字符的校验规则,可以校验信息的正确性,校验邮箱的合法,以及电话号码,金额等等
  1. 引入
  • 不使用正则与使用正则的区别
public class RegexDemo {
    public static void main(String[] args) {
        System.out.println(checkQQ("4546s4674"));
        System.out.println(checkQQRegex("45464674"));

    }

    public static boolean checkQQRegex(String qq){
        return qq!=null && qq.matches("\\d{4,}");
    }

    //演示不用正则表达式
    public static boolean checkQQ(String qq){//用整型接容易越界了
        //1.判断是否为null
        if (qq == null){
            return false;
        }else {
         //2.判断长度是否
            if(qq.length()>= 4){
                //3.判断是否全部是数字
                //先定义一个变量存储最终校验的结果
                boolean rs = true;//认为不合法!
                //4.遍历字符串中的每个字符
                for(int i =0;i<qq.length();i++){
                    char ch = qq.charAt(i);//根据索引取字符
                    if(ch < '0' || ch > '9'){
                        rs = false;//不合法
                    }
                }
                return rs;
            }else {
                return false;
            }
        }

    }
}
  1. 深入学习正则
  • 常用正则
public class RegexDemo02 {
    public static void main(String[] args) {
        //public boolean matches(String regex):判断是否和正则表达式匹配,匹配返回true
        //[abc] a,b或c
        System.out.println("a".matches("[abc]"));//true,包含abc
        System.out.println("z".matches("[abc]"));//false,没有包含abc

        //除了a,b,c
        System.out.println("a".matches("[^abc]"));//不能出现abc,false
        System.out.println("z".matches("[^abc]"));//true

        //\d表示0-9数字,\w表示数字或者字母
        System.out.println("a".matches("\\d"));//第一个\表示转义,后面的\d是一个整体,false
        System.out.println("3".matches("\\d"));//第一个\表示转义,后面的\d是一个整体,true
        System.out.println(("z").matches("\\w"));//第一个\表示转义,后面的\w是一个整体,true
        System.out.println(("2").matches("\\w"));//true
        System.out.println(("21").matches("\\w"));//false,默认校验一个,两个就不行了
        System.out.println(("你").matches("\\w"));//flase

        //校验密码
        //必须是数组 字母 下划线 至少6位
        System.out.println("33fd9sd".matches("\\w{6,}"));//true
        System.out.println("33fsd".matches("\\w{6,}"));//flase
        System.out.println("33f哈sd".matches("\\w{6,}"));//flase

        //验证,必须是数字和字符 必须是4位
        System.out.println("4fg1".matches("[a-zA-Z0-9]]{4}"));//\\w不行,有下划线,把下划线去掉

    }
}
  • 实际运用
public class RegexDemo03 {
    public static void main(String[] args) {
        //checkEmail();
        //checkTel();
        checkPhone();
    }

    //检验电话号码
    public static void checkPhone(){

        Scanner sc = new Scanner(System.in);
        System.out.println("请您输入电话号码:");
        String phone = sc.nextLine();
        if (phone.matches("0\\d{2,5}-?\\d{5,15}")){
            System.out.println("号码合法了");
        }else {
            System.err.println("你的号码格式不正确");//打出来的是红色的
        }
    }
    //校验手机号码
    public static void checkTel(){

        Scanner sc = new Scanner(System.in);
        System.out.println("请您输入手机号码:");
        String tel = sc.nextLine();
        if (tel.matches("1[3-9]\\d{9}")){
            System.out.println("号码合法了");
        }else {
            System.err.println("你的号码格式不正确");//打出来的是红色的
        }
    }

    //校验邮箱
    public static void checkEmail(){

        Scanner sc = new Scanner(System.in);
        System.out.println("请您输入邮箱:");
        String email = sc.nextLine();
        //45435@qq.com
        //fdsfsd@163.com
        //fsd5g@pic.com.cn
        if (email.matches("\\w{1,}@\\w{2,}(\\.\\w{2,10}){1,2}")){
            System.out.println("邮箱合法了");
        }else {
            System.err.println("你的邮箱格式不正确");//打出来的是红色的
        }
    }
}
  1. 正则表达式在方法中的应用
  • split()以及repanceAll()
public class RegexDemo04 {
    public static void main(String[] args) {
        //1.split的基础用法
        String names = "王菲,韩栋,韩寒";
        //以“,”分割
        String[] nameArrs = names.split(",");
        for (int i = 0;i < nameArrs.length;i++){
            String name = nameArrs[i];
            System.out.println(name);
        }

        System.out.println("-----------------------------------------");
        //2.split结合正则表达式做分割
        String names1 = "王菲545韩栋234234韩寒";
        //以匹配正则表达式的内容为分割点,分割成字符串数组
        String[] nameArrs1 = names1.split("\\w+");//+表示一次或者多次
        for (int i = 0;i < nameArrs1.length;i++){
            String name = nameArrs[i];
            System.out.println(name);
        }


        //3.public String repanceAll(String regex,String newStr)替换
        String names2 = "王菲54df5韩栋2fsdf34234韩寒";
        //使用正则表达式定位出不固定内容,替换成/
        System.out.println(names2.replaceAll("\\w+","/"));

        String names3 = "王菲54df5韩栋2fsdf34234韩寒";
    }
}
  1. 正则表达式爬取信息中的内容
public class RegexDemo05 {
    public static void main(String[] args) {
        String rs = "过来程序学习Java,电话020-8208820,或者联系邮箱"+
                "itcast@itcast.cn,电话13545675356,54353453"+
                "邮箱bozai@itcast.cn,400-100-3233,4001003232";
        //需求:从上面的内容中爬取出电话号码和邮箱
        //1.定义爬取规则
        String regex = "(\\w{1,}@\\w{2,}(\\.\\w{2,10}){1,2})|(1[3-9]\\d{9})|(0\\d{2,5}-?\\d{5,15}|400-?\\d{3,8}-?\\d{3,8})";
        //2.把正则表达式成为一个匹配规则对象
        Pattern pattern = Pattern.compile(regex);
        //3.通过匹配规则对象得到一个匹配数据内容的匹配器对象
        Matcher matcher = pattern.matcher(rs);
        //4.通过匹配器去内容中爬取出信息
        while (matcher.find()){//用循环写出来
            System.out.println(matcher.group());
        }
    }
}

9 泛型

9.1 基本泛型

  1. 概念
    什么是泛型?
        泛型就是一个标签<数据类型>
        泛型可以在编译阶段约束只能操作某种数据类型
    注意:JDK1.7开始,泛型后面的申请可以省略不写!
        泛型和集合都只能支持引用数据类型,不支持基本数据类型
  1. 案例
  • 简单案例
public class GenericityDemo {
    public static void main(String[] args) {
        //如果ArrayList不加泛型,什么都可以往里面扔,字符串,布尔类型,字符,整形
        ArrayList lists = new ArrayList();
        lists.add("java");
        lists.add(false);
        lists.add('a');
        lists.add(99);
        System.out.println(lists);

        //ArrayList<String> list = new ArrayList<String>();
        ArrayList<String> list = new ArrayList<>();//后面的String可以不写
        //ArrayList<int> list2 = new ArrayList<>();泛型里面不允许放基本数据类型,泛型和集合都是对象,对象就是引用数据类型了
        list.add("java");
        //list.add(false);报错啦,只能放字符
        //list.add(99);
        System.out.println(list);

        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(10);//已经是对象10,自动装箱,把基本数据类型装成对象了

    }
}

9.2 自定义泛型

  1. 概念
    引入:
        作用:约束别人传入的是指定的引用数据类型
    泛型类的概念:
        使用泛型定义的类就是泛型类
    泛型类的格式:
        修饰符 class 类名<泛型变量>{
            }
        泛型变量建议使用E,T,K,V,N
    需求:模拟ArrayList集合自定义一个集合MyArrayList集合
    泛型类的核心思想:是把出现泛型变量的地方全部替换成传输的真实的数据类型
            
            
    什么是泛型方法:
        定义了泛型的方法就是泛型方法
    泛型方法的定义格式:
        修饰符 <泛型变量> 返回值类型 方法名称(形参实参){
        }
    注意:方法定义了是什么泛型变量,后面就是什么泛型变量
    泛型类的核心思想:是把出现泛型变量的地方全部替换成传输的真实数据类型。
        
        
    什么是泛型接口?
        使用了泛型定义的接口就是泛型接口
    泛型接口的格式:
        修饰符 interface 接口名称<泛型变量>{
  1. 案例
  • 模拟ArrayList自定义泛型类
public class GenericityDemo03 {
    public static void main(String[] args) {
        MyArrayList<String> lists1 = new MyArrayList<>();
        lists1.add("java");//只能添加字符串的原因是下面的类中的E是传到了方法中,因此调用的时候方法也是能是String
        lists1.add("mysql");
        System.out.println(lists1);
        //lists.add(12.3);//报错
        lists1.remove("mysql");
        System.out.println(lists1);
    }
}

class MyArrayList<E>{//只是换了一个壳子

    private ArrayList lists = new ArrayList();//借用java底层的数组

    public void add(E e){
        lists.add(e);
    }

    public void remove(E e){
        lists.remove(e);
    }

    @Override
    public String toString() {
        return lists.toString();//又返回去了
    }
}
  • 自定义泛型方法
public class GenericityDemo04 {
    public static void main(String[] args) {
        Integer[] nums ={11,2,4,53,65};
        String rs1 = arrToString(nums);
        System.out.println(rs1);

        String[] names = {"郑爽","范冰冰","华晨宇"};
        String rs2 = arrToString(names);//所有类型就全部可以传进去了
        System.out.println(rs2);

    }

    public static <T> String arrToString(T[] nums){//定义一个泛型变量
        //字符串的拼接
        StringBuilder sb = new StringBuilder();
        sb.append("[");
        if(nums!=null && nums.length>0){
            for (int i = 0; i < nums.length; i++) {
                T ele = nums[i];
                sb.append(i==nums.length-1?ele:ele+",");
            }
        }
        sb.append("]");
        return sb.toString();//把sb打印成字符串输出
    }
}
  • 自定义泛型接口
public class Student {

}
public class Teacher {
}
public interface Data<E> {
    //void add(Student stu);
    //void add(Teacher tea);
    //只能添加一类数据,因此接口约束的类型带上<E>
    void add(E stu);
    void delete(E stu);
    void update(E stu);
    E query(int id);
//操作学生数据
public class StudentData implements Data<Student>{
    //重写的方法是对学生进行增删改查
    @Override
    public void add(Student stu) {
        System.out.println("添加学生");
    }

    @Override
    public void delete(Student stu) {
        System.out.println("删除学生");
    }

    @Override
    public void update(Student stu) {

    }

    @Override//返回学生
    public Student query(int id) {
        return null;
    }
}
//实现Data接口,对老师的数据进行操作
public class TeacherData implements Data<Teacher>{

    //重写的方法是对老师操作
    @Override
    public void add(Teacher stu) {
        System.out.println("添加老师!");
    }

    @Override
    public void delete(Teacher stu) {
        System.out.println("删除学生");
    }

    @Override
    public void update(Teacher stu) {

    }

    @Override
    public Teacher query(int id) {
        return null;
    }
}
  • 使用通配符?自定义泛型
public class GenericDemo {
    public static void main(String[] args) {
        ArrayList<BMW> bmws = new ArrayList<>();
        bmws.add(new BMW());
        bmws.add(new BMW());
        bmws.add(new BMW());

        ArrayList<BENS> bens = new ArrayList<>();
        bens.add(new BENS());
        bens.add(new BENS());
        bens.add(new BENS());


        ArrayList<Dog> dogs = new ArrayList<>();
        dogs.add(new Dog());
        dogs.add(new Dog());
        dogs.add(new Dog());

        //run(bens);报错

        //run(bens);//方法run使用ArrayList<Car>也是不行的,直接bens和bmws都不行

        //run(dogs);//狗也能进来在使用:public static void run(ArrayList<?> cars){的时候

        run(bmws);
        run(bens);
    }

    //定义一个方法,可以让很多汽车一起进入参加比赛
    //public static void run(ArrayList<BMW> cars){//只能接宝马
    //public static void run(ArrayList<Car> cars){//只能接宝马
    public static void run(ArrayList<? extends Car> cars){

    }
}


class Car{

}


class BMW extends Car{

}

class BENS extends Car{

}

class Dog{

}

10 集合

10.1 Collection集合

10.1.1 总体概念

什么是集合?
        集合是一个大小可变的容器。
        容器中的每个数据称为一个元素,数据==元素
        集合的特点:类型可以不确定,大小也是
        数组:类型和长度一旦定义出来就都固定了
    集合有啥用?
        开发中,很多元素的个数是不确定的
        而且经常要进行元素的增删改查,集合都是非常合适的
        开发中集合用的更多!

    Java中的集合的代表是: Collection
    Collection集合是Java中集合的祖宗类
    学习Collection集合的功能,那么一切集合都可以yong这些功能!

    Collection集合的体系:
                                    Collection<E>(接口)
                            /                                           \
                        Set<E>(接口)                                  List<E>(接口)
                /                   \                           /               \
        HashSet<E>(实现类)      TreeSet<>(实现类)       ArrayList<E>(实现类)      LinkedList<>(实现类)
            /
 LindedHashSet<>(实现类)

    集合的特点:
        Set系列集合:添加的元素是无序,不重复,无索引的  
            -- HashSet:添加的元素是无序的,不重复,无索引
            -- LinkedSet:添加的元素是有序,不重复,无索引
            -- TreeSet:默认按照大小排序,不重复,无索引
        List系列集合:添加的元素是有序的,可重复,有索引的
            -- ArrayList:添加的元素是有序的,可重复,有索引
            --LinkedList:添加的元素是有序的,可重复,有索引

    小结:
        先学Collection集合

10.1.2 Collection常用API

  1. 简单案例
public class CollectionDemo01 {
    public static void main(String[] args) {
        Collection<String> sets = new HashSet<>();
        sets.add("MyBatis");
        sets.add("Java");
        sets.add("Spring");
        sets.add("Java");
        sets.add("MySQL");
        sets.add("MySQL");

        System.out.println(sets);


        Collection<String> lists = new ArrayList<>();
        lists.add("MyBatis");
        lists.add("Java");
        lists.add("Spring");
        System.out.println(lists.add("Java"));
        lists.add("MySQL");
        lists.add("MySQL");
        System.out.println(lists);
    }
}
  1. Collection常用API
public class CollectionDemo {
    public static void main(String[] args) {
        //有序,可重复,有索引
        Collection<String> sets = new HashSet<>();
        //1.添加元素,添加成功返回true
        System.out.println(sets.add("赵薇"));
        System.out.println(sets.add("赵薇"));
        System.out.println(sets.add("嘻嘻"));
        System.out.println(sets.add("金晨"));
        System.out.println(sets);//打的内容,说明集合重写了toString

        //2.清空集合的元素
        //sets.clear();
        //System.out.println(sets);

        //3.判断集合是否为空,为空为true
        System.out.println(sets.isEmpty());

        //4.判断集合的大小
        System.out.println(sets.size());//3

        //5.判断集合中是否包含某个元素
        System.out.println(sets.contains("贾乃亮"));

        //6.删除某个元素:如果有重复,默认删除前面的第一个
        sets.remove("嘻嘻");
        System.out.println(sets);

        //7.把集合转成数组
        Object[] arrs = sets.toArray();
        System.out.println("数组:"+ Arrays.toString(arrs));

        //8.把集合转字符串
        //String[] arrs1 = sets.toArray(String[]::new);JDK1.8才能用,以后在了解
        //System.out.println("数组:"+ Arrays.toString(arrs1));


        System.out.println("============拓展==============");
        Collection<String> c1 = new ArrayList<>();
        c1.add("张无忌");
        c1.add("段誉");
        c1.add("楚留香");

        Collection<String> c2 = new ArrayList<>();
        c2.add("赵敏");
        c2.add("王语嫣");
        c2.add("张洁洁");

        c1.addAll(c2);//把c2集合的元素全部倒入到c1,合并集合用的
        System.out.println(c1);

    }
}

10.1.3 遍历的方式

  1. while+hasNext()
public class CollectionDemo01 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("殷素素");
        lists.add("周芷若");
        System.out.println(lists);

        //1.得到集合的迭代器对象
        Iterator<String> it = lists.iterator();
        //String ele = it.next();
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
//        System.out.println(it.next());
        //System.out.println(it.next());//如果第五个,就会出现NoSuchElementException没有雌螈属异常,出现没有此元素异常

        //2.使用while循环
        while (it.hasNext()){
            String ele = it.next();
            System.out.println(ele);
        }
    }
}
  1. foreach循环
public class CollectionDemo02 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("殷素素");
        lists.add("周芷若");
        System.out.println(lists);

        for(String x:lists){//x就像游标一样//快捷方式:lists.for
            System.out.println(x);
        }

        int[] ages = new int[]{17,18,38,21};
        for (int x : ages) {
            System.out.println(x);
        }
    }
}
  1. lambda表达式的遍历
public class CollectionDemo03 {
    public static void main(String[] args) {
        Collection<String> lists = new ArrayList<>();
        lists.add("赵敏");
        lists.add("小昭");
        lists.add("殷素素");
        lists.add("周芷若");
        System.out.println(lists);

        lists.forEach(new Consumer<String>() {///Consumer也是一个接口,用的Comsumer接口进行遍历的
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        //简化匿名内部类
        lists.forEach((String s)->{
                System.out.println(s);
        });

        //简化类型
        lists.forEach((s)->{
            System.out.println(s);
        });

        //简化小括号,因为只有一个参数
        lists.forEach(s->{
            System.out.println(s);
        });

        //简化大括号和;,因为只有一行代码
        lists.forEach(s->
            System.out.println(s)
        );

        //方法引用再次简写,这也太装了
        lists.forEach(System.out::println);
    }
}

10.1.4 补充:数据结构

/**
    目标:常见的数据结构

    集合是基于数据结构做出来的,不同的集合底层会采用不同的数据结构
    不同的数据结构,功能和作用是不一样的

    什么是数据结构?
        数据结构是指数据以什么方式组织在一起
        不同的数据结构,增删改的性能是不一样的
        不同的集合底层采用不同的数据结构,我们要知道集合的底层是基于哪种数据结构存储和操作数据的。
        这样才能知道具体场景用哪种集合。

    Java常见的数据结构有哪些?
    数据结构常用结构有:栈、对列、数组、链表和红黑树

    a.对列【queue】
        -- 先进先出,后进后出
        -- 场景:各种排队。叫号场景
        -- 有很多集合可以实现队列      水管中的水
    b.栈【stack】
        -- 后进先出,先进后出
        -- 压栈 ==入栈
        -- 弹栈 ==出栈
        -- 场景:手枪的弹夹  杯子中的水
    c.线性表【数据】
        -- 数组是内存中的连续存储的区域
        -- 分成若干等分的小区域,每个区域大小一样
        -- 元素存在索引 012345
        -- 特点:
            ①查询元素快,根据索引去查  例如arr[3] = 起始地址 + 3 个单位的长度
            ②增删元素慢,创建新数组,迁移元素
    d.链表
        -- 元素不是内存中的连续区域存储
        -- 元素是游离存储的。每个元素会记录下元素的地址。
        -- 特点:
            ①查询元素慢  不能根据索引知道位置
            ②增删元素快(针对于首尾元素,速度极快,一般是双链表)   找的时候慢,删的时候贼快

        数组全部连在一起,好查,但是改动的时候要全部都移位,链表不连在一起,不好查询,但是改动的时候只需要改一小部分
    e.红黑树
        二叉树:binary tree永远只有一个根节点,是每个结点不超过2个节点的tree
        查找二叉树/排序二叉树:小的左边,大的右边,但是可能树很高,性能变差
        平衡排序二叉树:为了排序和搜索会进行左旋和右旋实现平衡查找二叉树,让树的高度差不大于1
        红黑树:【基于红黑规则实现类自平衡的排序二叉树】
            树尽量保证到了很矮小,但是又排序好了,性能最高的树

        增删改查性能都好!
 */
public class DataStructure {
}

10.2 List集合

10.2.1 ArrayList

  1. 概念
List集合继承了Collection集合的全部功能,同时因为List系列集合有索引
    ArrayList集合底层基于数组存储数据的,查询快,增删慢!
    因为List集合多了索引,所以多了很多按照索引操作元素的功能。
        -- public void add(int index,E element):将指定的元素,添加到该集合中的指定位置上
        -- public E get(int index):返回集合中指定位置的元素
        -- public E remove(int index):移除列表中指定位置的元素,返回的是被移除的元素
        -- public E set(int index,E element):用指定元素替换集合中指定位置的元素,返回值的被修改的值

    小结:List系列集合,有序,可重复,有索引。原来Collection的集合也是可以的
        ArrayList集合底层基于数组存储数据的,查询快,增删慢!
        开发中ArrayList集合用的最多!
  1. 案例
public class ListDemo01 {
    public static void main(String[] args) {
        //1.创建一个ArrayList集合对象:经典代码就这么写!
        //List:有序,重复,有索引
        List<String> lists = new ArrayList<>();//因为ArrayList是完全继承了List接口的,没有新加什么功能,因此最前面写List
        lists.add("java1");//lists是可以重复的,有序的,有索引
        lists.add("java1");
        lists.add("java2");
        lists.add("java2");
        System.out.println(lists);//[java1, java1, java2, java2]
        //2.在某个索引中插入元素public void add(int index,E element)
        lists.add(2,"MySQL");
        System.out.println(lists);//[java1, java1, MySQL, java2, java2]
        //3.根据索引删除元素,返回被删除的元素public E remove(int index)
        System.out.println(lists.remove(2));
        System.out.println(lists);
        //4.根据索引获取元素
        System.out.println(lists.get(2));//java2
        //5.修改索引位置处的元素
        lists.set(3,"Mybatis");
        //System.out.println(lists.set(3, "Mybatis"));一般懒得返回,因为返回值是修改的原值
        System.out.println(lists);

    }
}

10.2.2 List循环的方式

public class ListDemo02 {
    public static void main(String[] args) {
        List<String> lists = new ArrayList<>();
        lists.add("java1");
        lists.add("java2");
        lists.add("java3");

        //1.for循环:因为list集合有索引,因此可以通过索引取值
        for(int i = 0;i<lists.size();i++){
            String ele = lists.get(i);
            System.out.println(ele);
        }
        System.out.println("----------------------");
        //2.迭代器
        Iterator it = lists.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("-------------------------");

        //3.增强for循环
            for(String x:lists){
                System.out.println(x);
            }
        System.out.println("-----------------------");

        //4.Lambda表达式
        lists.forEach(s->{
            System.out.println(s);
        });
    }
}

10.2.3 LinkedList

  1. 概念
LinkedList也是List的实现类:底层是基于链表的,与ArrayList的区别是,ArrayList底层是基于数组的,
                            基于链表的话,增删比较快,查询慢。
                            LinkedList是支持双链表的,定位前后的元素也是很快的,增删首尾的元素也是很快的
    因此,除了List集合的全部功能还多了很多操作首尾元素的特殊功能
        addFirst(E e):将指定元素插入到此列表的开头
        addLast(E e):将指定元素添加到此列表的结尾
        getFirst():返回此列表的第一个元素
        getLast():返回此列表的最后一个元素
        removeFirst():移除并返回此列表的第一个元素
        removeLast():溢出并返回此列表的最后一个元素
        pop():linkedlist天然的实现的栈和对列的功能,从此列表中表示堆栈处弹出一个元素
        push():将元素推如此列表所表示的堆栈

    小结:LinkedList是支持双链表的,定位前后的元素非常快,增删首尾的元素也是最快的。
        所以提供了很多操作首尾元素的API可以做栈和对列的实现。

        如果查询多,增删少,用ArrayList集合
        如果查询少,增删多,用LinkedList集合
  1. 代码
public class ListDemo03 {
    public static void main(String[] args) {
        //用LinkedList做一个对列
        //List<String> linkedList = new LinkedList<>();//首位操作的api是针对linkedlist的,所以不用多态的写
        LinkedList<String> queue = new LinkedList<>();
        //入队,往最后加,第一个也要从最后加
        queue.addLast("1号");
        queue.addLast("2号");
        queue.addLast("3号");
        queue.addLast("4号");
        System.out.println(queue);//[1号, 2号, 3号, 4号]
        //出队
        //System.out.println(queue.getFirst());//如果用getFirst拿取出来的永远是1号
        System.out.println(queue.removeFirst());
        System.out.println(queue.removeFirst());
        System.out.println(queue);

        //做一个栈
        LinkedList<String> stack = new LinkedList<>();
        //压栈
        stack.push("第一颗子弹");
        stack.push("第二颗子弹");
        stack.push("第三颗子弹");
        stack.push("第四颗子弹");//与addFirst一样
        System.out.println(stack);

        //弹栈
        System.out.println(stack.pop());//与removeFirst一样
        System.out.println(stack.pop());
        System.out.println(stack);
        
    }
}

10.3 Set集合

10.3.1 概念

     HashSet:添加的元素是无序的,不重复,无索引
         
     1.Set集合添加的元素是不重复的,是如何去掉重复的?

    答:对于有值特征的,Set集合可以直接判断进行去重复,例如add(1)这种
        对于引用数据类型的类对象,Set集合是按照如下流程进行是否重复的判断
            Set会让两两对象,先调用自己的hashCode()方法得到彼此的哈希值(所谓的内存地址)
            然后在比较两个对象的哈希值是否相同,如果不相同则直接认为两个对象不重复

            再然后:如果哈希值相同,会继续比较两个对象进行equals比较内容是否相同,如果还是相同认为是重复了,
                    如果不相同认为不重复。

        需求:只要对象内容一样,就希望集合认为它们重复了,重写hashCode和equals方法

    小结:
        如果希望Set集合认为两个对象只要内容一样就重复了,就必须重写hashCode和equals方法
         
         

10.3.2 HashSet()案例

  1. 简单案例
public class HashSetDemo01 {
    public static void main(String[] args) {
        //经典一行代码,无序,不重复,无索引的
        Set<String> sets = new HashSet<>();

        sets.add("Mybatis");
        sets.add("Java");
        sets.add("MySQL");
        sets.add("Spring");
        System.out.println(sets);
    }
}
  1. 是否重复

哈希值相同和内容是否相同才认为是重复

public class HashSetDemo02 {
    public static void main(String[] args) {
        Set<Integer> sets = new HashSet<>();
        sets.add(1);
        sets.add(2);
        sets.add(3);
        System.out.println(sets);

        //存储一些自定义数据类型
        Set<Apple> apples = new HashSet<>();
        Apple a1 = new Apple("红富士",59.9,"红色");
        Apple a2 = new Apple("阿克苏",39.9,"青红色");
        Apple a3 = new Apple("阿克苏",39.9,"青红色");
        apples.add(a1);
        apples.add(a1);//地址一样认为重复
        System.out.println(a1.hashCode());//哈希值,内存地址
        apples.add(a2);//没重写hashCode方法的时候,地址不一样不重复
        apples.add(a3);//没重写hashCode方法的时候,地址不一样不重复
        apples.add(a1);
        System.out.println(apples);
//没重写hashCode方法的时候,[Apple{name = 阿克苏, price = 39.9, color = 青红色}, Apple{name = 阿克苏, price = 39.9, color = 青红色}, Apple{name = 红富士, price = 59.9, color = 红色}]
        
//重写后hashCode方法:[Apple{name = 阿克苏, price = 39.9, color = 青红色}, Apple{name = 红富士, price = 59.9, color = 红色}]
//保留了第二个,覆盖了第一个
    }
}

重写了equals和hashcode()只要两个对象的内容一样就默认相同

public class Apple {
    private String name;
    private double price;
    private String color;


    public Apple() {
    }

    public Apple(String name, double price, String color) {
        this.name = name;
        this.price = price;
        this.color = color;
    }

    //只要两个对象内容一样,equals的比较的结果就是true
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Apple apple = (Apple) o;
        return Double.compare(apple.price, price) == 0 && Objects.equals(name, apple.name) && Objects.equals(color, apple.color);
    }

    //重写过的,只要两个对象的内容一样,返回的哈希值也要一样!
    @Override
    public int hashCode() {
        //原理:因为传入的参数都是一样的,返回的也应该是一样的
        return Objects.hash(name, price, color);
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return price
     */
    public double getPrice() {
        return price;
    }

    /**
     * 设置
     * @param price
     */
    public void setPrice(double price) {
        this.price = price;
    }

    /**
     * 获取
     * @return color
     */
    public String getColor() {
        return color;
    }

    /**
     * 设置
     * @param color
     */
    public void setColor(String color) {
        this.color = color;
    }

    public String toString() {
        return "Apple{name = " + name + ", price = " + price + ", color = " + color + "}";
    }
}
  1. 无序的原因
    是因为底层采用了哈希表存储元素。

    JDK1.8之前:哈希表 = 数组+链表+(哈希算法)
    JDK1.8之后:哈希表 = 数组+链表+红黑树+(哈希算法)
        当链表长度超过阈值【8】时候,将链表转成红黑树,这样大大减少了查找的时间
        哈希表底层是基于node数组

    小结:HashSet集合增删改查都比较快,但是是无序不重复的,

10.2.3 LinkedHashSet

  1. 概念
HashSet的子类,元素是”有序“不重复,无索引

     底层还是用的哈希表存储数据的。
     但是每个元素都额外带一个链来维护添加元素的添加顺序,多了个指针引导顺序
     不光增删查快,缺点是多了一个存储顺序的链会占用内存空间!但是不重复,无索引
  1. 代码
public class HashSetDemo04 {
    public static void main(String[] args) {
        //有序,不重复,无索引的
        Set<String> sets = new LinkedHashSet<>();

        sets.add("Mybatis");
        sets.add("Java");
        sets.add("MySQL");
        sets.add("Spring");
        System.out.println(sets);
    }
}

10.2.4 TreeSet

  1. 概念
TreeSet:不重复,无索引,按照大小默认升序排序!
    TreeSet集合称为排序不重复集合,可以对元素进行默认的升序排序

    TreeSet集合自排序的方式:
 、      1.有值特性的元素直接可以升序排序。(浮点型,整型)
        2.字符串类型的元素会按照首字符的编号排序
        3.对于自定义的引用数据类型,TreeSet集合默认无法排序,执行的时候直接报错,因为人家不知道排序规则
            所以要定制排序的大小规则:程序员定义大小规则的方案有两种:
            a.直接为对象的类实现比较器规则接口Comparable,重写比较器方法【拓展方案】
                //比较规则:Java规则,程序员认为比较者大于被比较者 返回正数!
                //比较规则:Java规则,程序员认为比较者小于被比较者 返回负数!
                //比较规则:Java规则,程序员认为比较者等于被比较者 返回0!
            b.直接为集合设置比较器Comarator对象,重写比较方法【拓展】,集合中的匿名内部类
                //比较规则:Java规则,程序员认为比较者大于被比较者 返回正数!
                //比较规则:Java规则,程序员认为比较者小于被比较者 返回负数!
                //比较规则:Java规则,程序员认为比较者等于被比较者 返回0!
            c.两者都有,默认使用集合的

10.4 Collections工具类

10.4.1 概念

Collections并不属于集合,是用来操作集合的工具类
    Collections有几个常用的API:都是静态方法,直接类调用
        public static <T> boolean addAll(Collection<? super T> c, T... elements)
        给集合对象批量添加元素
        public static void shuffle(List<?> list) 打乱集合顺序
        public static <T extends Comparable<? super T>> void sort(List<T> list) 将集合中元素按照默认规则排序
        但是不适合自定义的引用类型

10.4.2 案例

  1. 基本数据类型
public class CollectionsDemo01 {
    public static void main(String[] args) {
        //1.给集合批量添加元素
        Collection<String> names = new ArrayList<>();
        /**
         参数一:被添加元素的集合
         参数二:可变参数,一批元素
         */
        Collections.addAll(names,"曹操","张飞","诸葛亮");//不用一个一个add的加了
        System.out.println(names);

        //2.打乱集合的顺序:
        //注意:只能打乱有序集合的List集合
        List<String> newnames = new ArrayList<>();
        Collections.addAll(newnames,"MrCao","MrFei","MrZhu");
        Collections.shuffle(newnames);//打乱顺序
        System.out.println(newnames);

        //3.给List集合升序排序
        List<Double> scores = new ArrayList<>();
        Collections.addAll(scores,98.5,54.5,68.8);
        Collections.sort(scores);//默认升序排序
        System.out.println(scores);
    }
}

  1. 引用数据类型
public class CollectionsDemo02 {
    public static void main(String[] args) {
        List<Orange> oranges= new ArrayList<>();
        Orange o1 = new Orange("红橘子", 2, "贼便宜");
        Orange o2 = new Orange("黄橘子", 3, "贼便宜");
        Orange o3 = new Orange("黄橘子", 3, "贼便宜");
        Orange o4 = new Orange("青橘子", 4, "贼便宜");
        Collections.addAll(oranges,o1,o2,o3,o4);
        Collections.sort(oranges);
        System.out.println(oranges);



        List<Orange> oranges1= new ArrayList<>();
        Orange o11 = new Orange("红橘子", 2, "贼便宜");
        Orange o22 = new Orange("黄橘子", 3, "贼便宜");
        Orange o33 = new Orange("黄橘子", 4, "贼便宜");
        Orange o44 = new Orange("青橘子", 4, "贼便宜");
        Collections.addAll(oranges1,o11,o22,o33,o44);
        //优先用方法自带的比较器对象Comparator而不会用类的
        Collections.sort(oranges1, new Comparator<Orange>() {
            @Override
            public int compare(Orange o1, Orange o2) {
                return (int) (o1.getWeight()- o2.getWeight());
            }
        });

        System.out.println(oranges1);

    }
}

10.5 斗地主游戏综合案例

  1. 需求
业务需求分析:
        斗地主的建牌,洗牌,发牌,排序,看牌
        牌数:总用54张牌
        点数:"3","4","5","6","7","8","9","10","J","Q","K","A","2"
        花色:"♥","♠","♣","♦"
        大小王:"🤡","🃏"
        点数分别要组合4种花色,大小王各一张
        斗地主:发出51张牌,剩下3张作为底牌

    功能:
        1.建牌
        2.洗牌
        3.定义3个玩家
        4.发牌
        5.排序
        6.看牌
    用面向对象设计案例:
        a.定义一个牌类,代表牌对象。一个牌对象代表一张牌
        b.定义一个集合54张牌。集合就一个就好,因为就打一副牌。
  1. 代码
  • Card
public class Card {
    //定义牌的点数和花色
    private String number;
    private String color;
    private int index;


     public Card() {
    }

    public Card(String number, String color, int index) {
        this.number = number;
        this.color = color;
        this.index = index;
    }

    /**
     * 获取
     * @return number
     */
    public String getNumber() {
        return number;
    }

    /**
     * 设置
     * @param number
     */
    public void setNumber(String number) {
        this.number = number;
    }

    /**
     * 获取
     * @return color
     */
    public String getColor() {
        return color;
    }

    /**
     * 设置
     * @param color
     */
    public void setColor(String color) {
        this.color = color;
    }

    /**
     * 获取
     * @return index
     */
    public int getIndex() {
        return index;
    }

    /**
     * 设置
     * @param index
     */
    public void setIndex(int index) {
        this.index = index;
    }

    public String toString() {
        return number+color;
    }
}
  • 核心代码
public class GameDemo {
    //1. 定义静态集合,存储54张牌对象,集合就要一个
    public static final List<Card> ALL_CARDS = new ArrayList<>();
    //2.建牌,用静态代码块初始化,和类一起加载美滋滋
    static {
        //a.定义一个数组存放牌的点数,类型确定,个数确定,这边用数组,因为类型确定,个数也是确定的
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        //b.定义一个数组存放花色
        String[] colors = {"♥","♠","♣","♦"};
        //c.定义一个变量来存放索引
        int index = 0;
        //d.组装花色和点数成牌对象存储到集合中去,遍历点数和花色,大循环套小循环
        for (String number : numbers) {
            for (String color : colors) {
                //创建一张牌对象封装点数和花色
                Card card = new Card(number,color,index++);
                ALL_CARDS.add(card);
            }
        }
        //d.单独加入大小王牌
        Collections.addAll(ALL_CARDS,new Card("","🤡",index++),new Card("","🃏",index++));
        System.out.println("输出新牌:"+ALL_CARDS);
    }

    public static void main(String[] args) {
     //3.洗牌[把新牌的顺序打乱]
        Collections.shuffle(ALL_CARDS);
        System.out.println("洗牌后:"+ALL_CARDS);
    //4.定义3个玩家
        List<Card> yueyue = new ArrayList<>();
        List<Card> honghong = new ArrayList<>();
        List<Card> junjun = new ArrayList<>();
    //5.发牌
        //每张牌在集合中有索引,所以可以用取余思想去发牌
        for (int i = 0;i<ALL_CARDS.size()-3;i++){//减3是为了留下三张底牌
            //得到当前的这张对象
            Card c = ALL_CARDS.get(i);
            //判断这张牌发给谁
            if (i % 3 == 0){
                //请yueyue接牌
                yueyue.add(c);
            }else if(i % 3 == 1){
                honghong.add(c);
            }else if(i % 3 == 2){
                junjun.add(c);
            }
        }
    //6.对牌进行排序
        //Collections.sort(yueyue);//报错,没有比较规则
        //或者在集合中重写,Collections.sort(yueyue, new Comparator<Card>() {};但是有三个集合对象,冗余
        //于是封装成方法再重写Comparator
        sortCards(yueyue);
        sortCards(honghong);
        sortCards(junjun);



    //7.看牌
        System.out.println("月月:"+yueyue);
        System.out.println("红红:"+honghong);
        System.out.println("君君:"+junjun);
        //剩下最后三张牌,按索引打出来
        System.out.println("底牌:"+ALL_CARDS.get(53)+"-"+ALL_CARDS.get(52)+"-"+ALL_CARDS.get(51));
        //另一种最后三张牌获取的方式:截取集合的最后三张牌到一个新的List中去,用subList的api
        List<Card> lastThreeCards = ALL_CARDS.subList(ALL_CARDS.size()-3,ALL_CARDS.size());
        System.out.println("底牌:"+lastThreeCards);




    }
    //对牌的List集合进行排序(升序排序)
    private static void sortCards(List<Card> card) {
        Collections.sort(card, new Comparator<Card>() {
            @Override
            public int compare(Card o1, Card o2) {
                  //这边尴尬住了,这些牌不全部是值,不能按照大小排序
                 //于是这边自定义规则给计算机,直接在牌里属性中加一个索引,使牌本身就有大小
                return o1.getIndex()- o2.getIndex();
            }

        });
    }
}

10.6 Map集合

10.6.1 总体概念

 Map集合是另一种集合体系
    Collection是单值集合体系

    Map集合是双列集合,每个元素包含两个值
    Map集合的每个元素的格式:key value 键值对元素
    Map集合也被称为“键值对集合”

    Map集合的完整格式key1=value1  key2 = value2 ,...

    Map集合有啥用?
    1.Map集合存储的信息更加的具体丰富
        Collection:yueyue,“女”
        Map:name=yueyue,sex=“女”
    2.Map集合很适合做购物车这样的系统
        MapMap集合的体系:
                    Map<K,v>(接口,Map集合的祖宗类)
                /                           \
            TreeMap                 HashMap<K,V>(实现类,经典的,用的最多)
                                            \
                                    LinkedHashMap<K,V>(实现类)

    Map集合的特点:
        1.Map集合的特点都是由键决定的,key
        2.Map集合的键是无序的,不重复的,无索引的
            Map集合后面重复的键队对应的元素会覆盖前面的整个元素
        3.Map集合的值是无要求的
        4.Map集合的键值对都可以为null

        HashMap:元素按照键是无序的,不重复的,无索引,值不做要求
        LinkedHashMap:元素按照键是有序的,不重复的,无索引的,值不做要求

10.6.2 案例

  1. 简单案例
public class MapDemo {
    public static void main(String[] args) {
        //一行经典代码
        Map<String,Integer> maps = new HashMap<>();
        maps.put("娃娃",1);
        maps.put("口红",2);
        maps.put("散粉",2);
        maps.put(null,null);
        System.out.println(maps);//无序的,不重复,可以为空,对值没要求
    }

}

结果

{null=null, 口红=2, 散粉=2, 娃娃=1}
  1. 常用API(重点)
public class MapDemo {
    public static void main(String[] args) {
        Map<String, Integer> maps = new HashMap<>();
        //1.添加元素:无序,不重复,无索引
        maps.put("娃娃",1);
        maps.put("口红",2);
        maps.put("散粉",3);
        maps.put("剃须刀",4);
        System.out.println(maps);

        //2.清空集合
        //maps.clear();
        //System.out.println(maps);

        //3.判断集合是否为空,为空返回true
        System.out.println(maps.isEmpty());//false

        //4.根据键获取对应值
        Integer value = maps.get("娃娃");
        System.out.println(value);
        System.out.println(maps.get("散粉"));

        //5.根据键删除整个元素,返回的是值
        maps.remove("口红");
        System.out.println(maps);

        //6.判断是否包含某个键,包含返回true,反之
        System.out.println(maps.containsKey("剃须刀"));

        //7.判断是否包含某个值
        System.out.println(maps.containsValue(4));

        //8.获取全部键的集合 public Set<K> keySet()
        //Map集合的键是无序不重复的,所以返回的是一个Set集合
        Set<String> keys = maps.keySet();
        for (String key : keys) {
            System.out.println(key);
        }

        //9.获取全部值的集合:Collection<V> values;
        //不能返回Set集合,因为值可能重复了
        Collection<Integer> values = maps.values();
        for (Integer x : values) {
            System.out.println(x);
        }

        //10.集合的大小
        System.out.println(maps.size());

        //11.合并其他集合
        Map<String,Integer> maps1 = new HashMap<>();
        maps1.put("肥皂盒",4);
        maps1.put("洗洁精",5);
        maps1.put("洗衣液",6);
        maps.putAll(maps1);//把Map集合maps1的数据倒入搭配maps中了
        System.out.println(maps);
    }
}

10.6.3 常用遍历方式

  1. 使用keySet()找key,然后遍历
public class MapDemo01 {
    public static void main(String[] args) {
        Map<String, Integer> maps = new HashMap<>();
        maps.put("娃娃",1);
        maps.put("口红",2);
        maps.put("散粉",3);
        maps.put("剃须刀",4);
        maps.put("肥皂盒",4);
        maps.put("洗洁精",5);
        maps.put("洗衣液",6);
        System.out.println(maps);

        //1."键找值"的方式遍历Map集合
        //a.先获取Map集合的全部键的Set集合
        Set<String> keys = maps.keySet();
        //b.通过遍历键然后通过键取对应的值
        for (String key : keys) {
            System.out.println(key+"="+maps.get(key));
        }
        
    }
}
  1. 转换成Set()遍历
    //2.键值对的遍历:更加面向对象的方式,代码有点复杂,不过问题不大
         //思想:直接把键值当作一个整理取遍历,但是增强for循环不能遍历Map集合
         // 因此通过Set<Map.Entry<K,V>>转成Set集合中的键值对实体类型从而让for遍历,就是使用maps.entrySet(),再用Set<>中的Map实体类型接收
        Set<Map.Entry<String, Integer>> entries = maps.entrySet();
        //这样就能遍历了!
        for (Map.Entry<String, Integer> entry : entries) {
            System.out.println(entry);
        }
  1. Lambda表达式
  //3.Lambda表达式,太香了!
            maps.forEach((k,v)->{
                System.out.println(k+"=>"+v);
            });
        }

10.6.4 Map集合存储自定义类型

  1. 自定义Orange类型,作为Map的存储类
public class Orange {
    private String name;
    private double weight;
    private String price;


    public Orange() {
    }

    public Orange(String name, double weight, String price) {
        this.name = name;
        this.weight = weight;
        this.price = price;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return weight
     */
    public double getWeight() {
        return weight;
    }

    /**
     * 设置
     * @param weight
     */
    public void setWeight(double weight) {
        this.weight = weight;
    }

    /**
     * 获取
     * @return price
     */
    public String getPrice() {
        return price;
    }

    /**
     * 设置
     * @param price
     */
    public void setPrice(String price) {
        this.price = price;
    }

    public String toString() {
        return "Orange{name = " + name + ", weight = " + weight + ", price = " + price + "}";
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Orange orange = (Orange) o;
        return Double.compare(orange.weight, weight) == 0 && Objects.equals(name, orange.name) && Objects.equals(price, orange.price);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, weight, price);
    }
}
  1. Orange类作为Map的键对象传入
public class MapDemo01 {
    public static void main(String[] args) {
        Map<Orange,String> maps = new HashMap<>();//这边把键作为自定义类型
        Orange o1 = new Orange("黄橘子", 21.3, "贼便宜");//重写hashCode和equal方法就会只剩下一个了
        Orange o2 = new Orange("黑橘子",31.3,"坏了");
        Orange o3 = new Orange("青橘子",24.3,"9.9包邮");
        Orange o4 = new Orange("黄橘子",21.3,"贼便宜");
        Orange o5 = new Orange("黄橘子",21.3,"贼便宜");


        maps.put(o1,"江西\n");
        maps.put(o2,"赣州\n");
        maps.put(o3,"广州\n");
        maps.put(o4,"广西\n");

        System.out.println(maps);//重写后,广西的会覆盖江西的那一条
    }
}

10.6.5 LinkedHashMap

  1. 特点
	LinkedHashMapHashMap的子类
        --添加的元素键有序,不重复
    HashSet集合相当于HashMap集合的键都不带值的
    LinkedHashSet相当于LinkedHashMap集合的键都不带值
        
    底层原理完全一样,都是基于哈希表按照键存储数据的,多了一个链来维护顺序
  1. 代码
public class LinkedHashMapDemo {
    public static void main(String[] args) {


        Map<String, Integer> maps = new LinkedHashMap<>();
        maps.put("娃娃", 1);
        maps.put("口红", 2);
        maps.put("散粉", 3);
        maps.put("剃须刀", 4);
        maps.put("肥皂盒", 4);
        maps.put("洗洁精", 5);
        maps.put("洗衣液", 6);
        System.out.println(maps);//有序
    }
}
  1. 结果
{娃娃=1, 口红=2, 散粉=3, 剃须刀=4, 肥皂盒=4, 洗洁精=5, 洗衣液=6}

10.6.6 TreeMap

  1. 特点
    TreeMap集合按照键是可排序不重复的键值对集合【默认升序】
    TreeMap集合按照键的特点是TreeSet是完全一样的
  1. 代码
  • Pig类
public class Pig implements Comparable{
    private String name;
    private double price;
    private double weight;


    public Pig() {
    }

    public Pig(String name, double price, double weight) {
        this.name = name;
        this.price = price;
        this.weight = weight;
    }

    /**
     * 获取
     * @return name
     */
    public String getName() {
        return name;
    }

    /**
     * 设置
     * @param name
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * 获取
     * @return price
     */
    public double getPrice() {
        return price;
    }

    /**
     * 设置
     * @param price
     */
    public void setPrice(double price) {
        this.price = price;
    }

    /**
     * 获取
     * @return weight
     */
    public double getWeight() {
        return weight;
    }

    /**
     * 设置
     * @param weight
     */
    public void setWeight(double weight) {
        this.weight = weight;
    }

    public String toString() {
        return "Pig{name = " + name + ", price = " + price + ", weight = " + weight + "}";
    }

    @Override
    public int compareTo(Object o) {
        //return this.price-((Pig)o).price;//99.5-99.4强转int0.1会没掉
        //浮点类型的比较由专门的api
        return Double.compare(this.price,((Pig)o).price);
    }
}
  • 排序
public class TreeMapDemo {
    public static void main(String[] args) {
        Map<Integer,String > maps = new TreeMap<>();
        maps.put(999,"张三");
        maps.put(888,"李四");
        maps.put(777,"王五");
        maps.put(666,"赵六");

        System.out.println(maps);

        //用的继承implements Comparable按照加个升序
        Map<Pig,String> pigs = new TreeMap<>();
        pigs.put(new Pig("小猪佩奇",99.5,500.0),"荷兰\n");
        pigs.put(new Pig("乔治",99.4,600.0),"澳大利亚\n");
        pigs.put(new Pig("野猪",199.4,300.0),"山上\n");

        System.out.println(pigs);//升序了

        //用的new Comparator,按照体重升序
        Map<Pig,String> pigs1 = new TreeMap<>(new Comparator<Pig>() {
            @Override
            public int compare(Pig p1, Pig p2) {
                return Double.compare(p1.getWeight(),p2.getWeight());
            }
        });
        pigs1.put(new Pig("小猪佩奇",99.5,500.0),"荷兰\n");
        pigs1.put(new Pig("乔治",99.4,600.0),"澳大利亚\n");
        pigs1.put(new Pig("野猪",199.4,300.0),"山上\n");
        System.out.println(pigs1);
        
    }
}
  1. 结果
{666=赵六, 777=王五, 888=李四, 999=张三}

{Pig{name = 乔治, price = 99.4, weight = 600.0}=澳大利亚, Pig{name = 小猪佩奇, price = 99.5, weight = 500.0}=荷兰, Pig{name = 野猪, price = 199.4, weight = 300.0}=山上}

{Pig{name = 野猪, price = 199.4, weight = 300.0}=山上, Pig{name = 小猪佩奇, price = 99.5, weight = 500.0}=荷兰, Pig{name = 乔治, price = 99.4, weight = 600.0}=澳大利亚}

10.7 经典案例

10.7.1 输出一个字符串中的每个字符出现次数

  • 需求
    目标:输出一个字符串中的每个字符出现次数

    分析:
        1.键盘录入一个字符串
        2.定义一个Map集合,键是每个字符,值是出现的次数
        3.遍历字符串中的每一个字符
        4.拿着这个字符去Map集合中看是否有这个字符键,有说明之前统计过,其值+1,没有这个字符串,说明该字符是第一次统计,直接该字符=1
  • 代码
public class MapDemo01 {
    public static void main(String[] args) {
        //1.键盘录入一个字符串
        Scanner scanner = new Scanner(System.in);
        System.out.println("请您输入一个字符串:");
        String datas = scanner.nextLine();

        //2.定义一个Map集合,键是每个字符,值是出现的次数
        Map<Character,Integer> infos = new HashMap<>();

        //3.遍历每个字符,字符串可以用索引
        //aabbccddaa123
        for(int i = 0;i < datas.length();i++){
            char ch = datas.charAt(i);
            //4.拿着这个字符去Map集合中看是否有这个字符键,有说明之前统计过,其值+1,没有这个字符串,说明该字符是第一次统计,直接该字符=1
            if(infos.containsKey(ch)){
                infos.put(ch,infos.get(ch)+1);
            }else {
                infos.put(ch,1);
            }
        }
        //5.输出结果
        System.out.println("结果:"+infos);
    }
}

10.7.2 用Map实现斗地主游戏

  1. 代码
public class GameDemo {
    //1.定义Map集合存储54张牌对象,键是牌对象,值是其大小
    public static final Map<Card,Integer> ALL_CARDS = new LinkedHashMap<>();
    //2.做牌
    static {
        //a.定义一个数组存放牌的点数,类型确定,个数确定,这边用数组,因为类型确定,个数也是确定的
        String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
        //b.定义一个数组存放花色
        String[] colors = {"♥","♠","♣","♦"};
        //c.定义一个变量来存放索引
        int index = 0;
        //d.组装花色和点数成牌对象存储到集合中去,遍历点数和花色,大循环套小循环
        for (String number : numbers) {
            for (String color : colors) {
                //创建一张牌对象封装点数和花色
                Card card = new Card(number,color);
                ALL_CARDS.put(card,index++);
            }
        }
        //d.单独加入大小王牌
        ALL_CARDS.put(new Card("","🤡"),52);
        ALL_CARDS.put(new Card("","🃏"),53);
        System.out.println("输出新牌:"+ALL_CARDS);
    }

    public static void main(String[] args) {
     //3.洗牌
    }
}

10.7.3 图书管理案例

详见:[(58条消息) JavaSE案例 02——“图书馆管理系统”之集合综合运用案例_:Concerto的博客-CSDN博客](

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值