day12 【set、HashSet、LinkedHashSet、TreeSet、综合练习】上课

1.Set集合介绍(掌握)

  • 特点:

    1.存取无序

    2.数据唯一

    3.不能使用索引操作

  • 位于java.util包下的,使用需要导包

  • Set集合中的方法和Collection集合中的方法一样,没有特有的方法

  • Set接口的子类:

    • HashSet重点
      • LinkedHashSet
    • TreeSet

2.HashSet集合(必须掌握)

1.概念介绍

  • HashSet集合底层其实是后面学习的HashMap集合,而HashMap集合底层是哈希表
  • HashSet集合能够保证数据唯一 存取无序都是由底层数据结构哈希表决定的,我们重点学习下哈希表数据结构
  • 向HashSet集合中存储的对象数据,需要依赖hashCode和equals方法来决定数据存储的位置和是否存储到哈希表中
  • 对于hashCode和equals方法我们自定义的类都需要重写Object类中,不使用Object类中的,我们希望都是根据对象的内容(数据)来计算和判断是否相等

代码演示:

package com.itheima.sh.hashset_01;

import java.util.HashSet;

/*
    HashSet集合特点:
    1.存取无序
    2.数据唯一
    3.不能使用索引操作
 */
public class HashSetDemo01 {
    public static void main(String[] args) {
        //创建HashSet集合类的对象
        //HashSet() 构造一个新的空 set,其底层 HashMap 实例的默认初始容量是 16,加载因子是 0.75。
        HashSet<String> hs = new HashSet<>();
        //添加数据
        hs.add("柳岩");
        hs.add("杨幂");
        hs.add("李小璐");
        hs.add("马蓉");
        hs.add("柳岩");
//        System.out.println("hs = " + hs);
        //使用增强for循环
        for (String s : hs) {
            System.out.println("s = " + s);
        }
    }
}

小结:

1.HashSet集合特点:
1.存取无序
2.数据唯一
3.不能使用索引操作

2.向HashSet集合中存储数据依赖于HashCode和equals方法

   /*
        HashCode方法演示
        结论:
            1.如果两个对象的哈希码值不一样,那么这两个对象肯定不同
            2.如果两个对象的哈希码值一样,那么两个对象有可能不相同
            3.对象调用hasCode方法计算出的值是一个比较大的整数值,我们称为哈希码值
     */
    private static void method_2() {
        int i = "柳岩".hashCode();//848662
        int i1 = "杨幂".hashCode();//844762
        int i2 = "杨幂".hashCode();//844762
        System.out.println(i);
        System.out.println(i1);
        System.out.println(i2);

        System.out.println("重地".hashCode());//1179395
        System.out.println("通话".hashCode());//1179395


    }

小结:
1.如果两个对象的哈希码值不一样,那么这两个对象肯定不同
2.如果两个对象的哈希码值一样,那么两个对象有可能不相同

2.HashSet集合存储数据的结构_哈希表

1.在jdk8前哈希表由:数组+链表 数组是主体,链表解决哈希冲突(两个对象的HashCode方法的哈希码值一样)

2.jdk8后哈希表由:数组+链表+红黑树,数组是主体,链表+红黑树来解决哈希冲突,引入红黑树的目的是为了提高查询效率。比链表快。当链表的节点个数大于8并且数组长度大于等于64,才将链表变为红黑树

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ermVgmdP-1592806598812)(/image-20200517100439615.png)]

3.哈希表的存储过程

代码演示:

package com.itheima.sh.hashset_01;

import java.util.HashSet;

/*
    哈希表的存储过程
 */
public class HashSetDemo02 {
    public static void main(String[] args) {
        //创建HashSet集合对象
        HashSet<String> hs = new HashSet<>();
        //添加数据
        hs.add("柳岩");
        hs.add("杨幂");
        hs.add("璐璐");
        hs.add("冰冰");
        hs.add("蓉蓉");
        hs.add("诗诗");
        hs.add("柳岩");
        System.out.println(hs);
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1rbk90MS-1592806598817)(/image-20200517103627431.png)]

4.向哈希表中存储自定义类的对象

代码演示:

package com.itheima.sh.hashset_01;

import java.util.Objects;

public class Student {
    String name;
    int age;

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    //重写equals和hashCode alt+insert
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}



package com.itheima.sh.hashset_01;

import java.util.HashSet;

/*
    需求:向HashSet集合中存储自定义类Student的对象
 */
public class HashSetDemo03 {
    public static void main(String[] args) {
        //创建HashSet集合
        HashSet<Student> hs = new HashSet<Student>();
        //添加数据
        Student s1 = new Student("张三", 19);
        Student s2 = new Student("李四", 20);
        Student s3 = new Student("王五", 19);
        Student s4 = new Student("张三", 19);
        //添加数据
        hs.add(s1);
        hs.add(s2);
        hs.add(s3);
        hs.add(s4);
        System.out.println(hs);
    }
}

小结:

只要向哈希表中存储对象数据,那么所属类必须重写hashCode和equals方法。

5.HashSet的源码分析

public class HashMap{
    //......
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
    //......
    static final int hash(Object key) {//根据参数,产生一个哈希值
        int h;
        /*
     		1)如果key等于null:
     			可以看到当key等于null的时候也是有哈希值的,返回的是0.
     		2)如果key不等于null:
     			首先计算出key的hashCode赋值给h,然后与h无符号右移16位后的二进制进行按位异或得到最后的					hash值
     		3)注意这里计算最后的hash值会结合下面的数组长度计算出存储数据的索引
     	*/
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }
    //......
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; //临时变量,存储"哈希表"——由此可见,哈希表是一个Node[]数组
        Node<K,V> p;//临时变量,用于存储从"哈希表"中获取的Node
        int n, i;//n存储哈希表长度;i存储哈希表索引
        /*
    	1)transient Node<K,V>[] table; 表示存储Map集合中元素的数组。
    	2)(tab = table) == null 表示将空的table赋值给tab,然后判断tab是否等于null,第一次肯定是			null
    	3)(n = tab.length) == 0 表示将数组的长度0赋值给n,然后判断n是否等于0,n等于0
    	由于if判断使用双或,满足一个即可,则执行代码 n = (tab = resize()).length; 进行数组初始化。
    	并将初始化好的数组长度赋值给n.
    	4)执行完n = (tab = resize()).length,数组tab每个空间都是null
    	*/
        if ((tab = table) == null || (n = tab.length) == 0)//判断当前是否还没有生成哈希表
            n = (tab = resize()).length;//resize()方法用于生成一个哈希表,默认长度:16,赋给n
    /*
    	1)i = (n - 1) & hash 表示计算数组的索引赋值给i,即确定元素存放在哪个桶中
    	2)p = tab[i = (n - 1) & hash]表示获取计算出的位置的数据赋值给节点p
    	3) (p = tab[i = (n - 1) & hash]) == null 判断节点位置是否等于null,如果为null,则执行代			码:tab[i] = newNode(hash, key, value, null);根据键值对创建新的节点放入该位置的桶中
        小结:如果当前桶没有哈希碰撞冲突,则直接把键值对插入空间位置
    */ 
        if ((p = tab[i = (n - 1) & hash]) == null)//(n-1)&hash等效于hash % n,转换为数组索引
            tab[i] = newNode(hash, key, value, null);//此位置没有元素,直接存储
        else {//否则此位置已经有元素了
            Node<K,V> e; K k;
       /*
        	比较桶中第一个元素(数组中的结点)的hash值和key是否相等
        	1)p.hash == hash :p.hash表示原来存在数据的hash值  hash表示后添加数据的hash值 比较两个				 hash值是否相等
                 说明:p表示tab[i],即 newNode(hash, key, value, null)方法返回的Node对象。
                    Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) 
                    {
                        return new Node<>(hash, key, value, next);
                    }
                    而在Node类中具有成员变量hash用来记录着之前数据的hash值的
             2)(k = p.key) == key :p.key获取原来数据的key赋值给k  key 表示后添加数据的key 比较两					个key的地址值是否相等
             3)key != null && key.equals(k):能够执行到这里说明两个key的地址值不相等,那么先判断后				添加的key是否等于null,如果不等于null再调用equals方法判断两个key的内容是否相等
        */
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))//判断哈希值和equals
               /*
                	说明:两个元素哈希值相等,并且key的值也相等
                	将旧的元素整体对象赋值给e,用e来记录
                */ 
                e = p;//将哈希表中的元素存储为e
            // hash值不相等或者key不相等;判断p是否为红黑树结点
            else if (p instanceof TreeNode)//判断是否为"树"结构
                // // 放入树中
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {//排除以上两种情况,将其存为新的Node节点
                //说明是链表节点
            /*
            	1)如果是链表的话需要遍历到最后节点然后插入
            	2)采用循环遍历的方式,判断链表中是否有重复的key
            */
                for (int binCount = 0; ; ++binCount) {//遍历链表
                /*
                	1)e = p.next 获取p的下一个元素赋值给e
                	2)(e = p.next) == null 判断p.next是否等于null,等于null,说明p没有下一个元					素,那么此时到达了链表的尾部,还没有找到重复的key,则说明HashMap没有包含该键
                	将该键值对插入链表中
                */
                    if ((e = p.next) == null) {//找到最后一个节点
                        /*
                    	1)创建一个新的节点插入到尾部
                    	 p.next = newNode(hash, key, value, null);
                    	 Node<K,V> newNode(int hash, K key, V value, Node<K,V> next) 
                    	 {
                                return new Node<>(hash, key, value, next);
                         }
                         注意第四个参数next是null,因为当前元素插入到链表末尾了,那么下一个节点肯定是								null
                         2)这种添加方式也满足链表数据结构的特点,每次向后添加新的元素
                    	*/
                        p.next = newNode(hash, key, value, null);//产生一个新节点,赋值到链表
                    /*
                    	1)节点添加完成之后判断此时节点个数是否大于TREEIFY_THRESHOLD临界值8,如果大于
                    	则将链表转换为红黑树
                    	2)int binCount = 0 :表示for循环的初始化值。从0开始计数。记录着遍历节点的个						数。值是0表示第一个节点,1表示第二个节点。。。。7表示第八个节点,加上数组中的的一						个元素,元素个数是9
                    	TREEIFY_THRESHOLD - 1 --》8 - 1 ---》7
                    	如果binCount的值是7(加上数组中的的一个元素,元素个数是9)
                    	TREEIFY_THRESHOLD - 1也是7,此时转换红黑树
                    */
                        if (binCount >= TREEIFY_THRESHOLD - 1) //判断链表长度是否大于了8
                            //转换为红黑树
                            treeifyBin(tab, hash);//树形化
                        //跳出循环
                        break;
                    }
                 /*
                	执行到这里说明e = p.next 不是null,不是最后一个元素。继续判断链表中结点的key值与插					  入的元素的key值是否相等
                */
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))//跟当前变量的元素比较,如果hashCode相同,equals也相同
                    // 相等,跳出循环
                    /*
                		要添加的元素和链表中的存在的元素的key相等了,则跳出for循环。不用再继续比较了
                		直接执行下面的if语句去替换去 if (e != null) 
                	*/
                        break;//结束循环
                /*
                	说明新添加的元素和当前节点不相等,继续查找下一个节点。
                	用于遍历桶中的链表,与前面的e = p.next组合,可以遍历链表
                */
                    p = e;//将p设为当前遍历的Node节点
                }
            }
        /*
        	表示在桶中找到key值、hash值与插入元素相等的结点
        	也就是说通过上面的操作找到了重复的键,所以这里就是把该键的值变为新的值,并返回旧值
        	这里完成了put方法的修改功能
        */
            if (e != null) {  
            // 记录e的value
            V oldValue = e.value;
            // onlyIfAbsent为false或者旧值为null
            if (!onlyIfAbsent || oldValue == null)
                //用新值替换旧值
                //e.value 表示旧值  value表示新值 
                e.value = value;
            // 访问后回调
            afterNodeAccess(e);
            // 返回旧值
            return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }
}

6.HashMap常见面试题(掌握)

1.哈希碰撞:对象的hashCode方法计算的值相等产生哈希碰撞

2.如何解决哈希碰撞:使用链表(8之前),使用链表+红黑树(8之后)

3.为什么引入红黑树?

目的是为了提高查找效率

4.何时链表变为红黑树?

节点大于大于8并且数组长度大于等于64

8.为什么红黑树设置的节点是8?

   * Because TreeNodes are about twice the size of regular nodes, we
     * use them only when bins contain enough nodes to warrant use
     * (see TREEIFY_THRESHOLD). And when they become too small (due to
     * removal or resizing) they are converted back to plain bins.  In
     * usages with well-distributed user hashCodes, tree bins are
     * rarely used.  Ideally, under random hashCodes, the frequency of
     * nodes in bins follows a Poisson distribution
     * (http://en.wikipedia.org/wiki/Poisson_distribution) with a
     * parameter of about 0.5 on average for the default resizing
     * threshold of 0.75, although with a large variance because of
     * resizing granularity. Ignoring variance, the expected
     * occurrences of list size k are (exp(-0.5) * pow(0.5, k) /
     * factorial(k)). The first values are:
     *
     * 0:    0.60653066
     * 1:    0.30326533
     * 2:    0.07581633
     * 3:    0.01263606
     * 4:    0.00157952
     * 5:    0.00015795
     * 6:    0.00001316
     * 7:    0.00000094
     * 8:    0.00000006

满足泊松分布,统计学。

9.什么是加载因子?

加载因子也称为负载因子,默认是0.75.他是根据数组长度计算出边界值,何时扩容的。

数组长度默认是16,边界值:16*0.75===》12

为什么加载因子设置为0.75呢?

因为0.75是最佳方案。

10.创建集合对象时,如果知道存储多少个数据,那么不建议使用空参的。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VGL05qHI-1592806598819)(/image-20200517115252124.png)]

集合长度一定是2的n次方。

假设这里给参数是7

HashSet(int initialCapacity)  7--->变为8    10---16
    底层会根据你指定的值变为比你指定的值大的最小的2的n次幂

3.LinkedHashSet(了解)

Set集合下面都是存取无序的,但是该集合是存取有序的。因为他底层有两个数据结构:

​ 1.链表:保证存取有序

​ 2.哈希表:保证数据唯一,真正存储数据

代码演示:

package com.itheima.sh.linkedhashset_02;

import java.util.LinkedHashSet;

public class LinkedHashSetDemo01 {
    public static void main(String[] args) {
        //创建集合对象
        LinkedHashSet<String> list = new LinkedHashSet<>();
        //添加数据
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
        list.add("aaa");
        System.out.println("list = " + list);//list = [aaa, bbb, ccc]
    }
}

小结:

1.LinkedHashSet是Set集合下面的存取有序的集合

​ 链表:保证存取有序

​ 哈希表:保证数据唯一,真正存储数据

4.TreeSet集合(了解)

1.TreeSet集合底层是TreeMap,TreeMap底层是红黑树数据结构

2.向该集合中存储的数据特点:

​ 1)数据唯一

​ 2)可以对数据进行大小排序,具体如何排序看使用的构造方法

1TreeSet()  构造一个新的空 set,默认排序方式,大小升序
2TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
    	Comparator表示自定义排序接口,该接口下有一个排序方法:
    	int compare(T o1, T o2)  
    		升序:o1 - o2
    		降序:o2 - o1

代码演示:

package com.itheima.sh.treeset_02;

import java.util.Comparator;
import java.util.TreeSet;

public class TreeSetDemo01 {
    public static void main(String[] args) {
        //调用方法
        method_3();
    }
    /*
        2)TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
    	Comparator表示自定义排序接口,该接口下有一个排序方法:
    	int compare(T o1, T o2)
    		升序:o1 - o2
    		降序:o2 - o1
     */
    private static void method_3() {
        /*
            TreeSet存储自定义类型时,必须要给出排序方式。
            需求:按照年龄从小到大排序,如果年龄相同,判断姓名是否相同,相同不存储,不同就存储
         */
        //创建集合对象
        TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //判断年龄是否相同,如果不同大小升序
                if(o1.age != o2.age){
                    //说明年龄不等
                    return o1.age - o2.age;
                }
                //如果执行到这里,说明年龄相同,判断姓名是否相同,相同就不存储,不同存储
                if(o1.name.equals(o2.name)){
                    //说明姓名相同 不存储
                    return 0;//这里一定是0
                }else{
                    //说明姓名不同 存储
                    return -1;//这里只要不返回0就可以,正数和负数都可以存储
                }
            }
        });
        //创建学生对象
        Student s1 = new Student("柳岩", 19);
        Student s2 = new Student("杨幂", 20);
        Student s3 = new Student("冰冰", 19);
        Student s4 = new Student("柳岩", 19);
        //添加数据
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        //输出
        System.out.println(ts);
    }

    /*
        2)TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
    	Comparator表示自定义排序接口,该接口下有一个排序方法:
    	int compare(T o1, T o2)
    		升序:o1 - o2
    		降序:o2 - o1
     */
    private static void method_2() {
        //比较器排序.要求:按照长度升序排序,长度相同的不存
        //创建集合对象
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                return o1.length() - o2.length();
            }
        });

        //添加数据
        ts.add("ajaha");
        ts.add("AJAH");
        ts.add("abc");
        ts.add("abcd");
        ts.add("abc");
        ts.add("哈哈哈");
        //输出 ts = [abc, AJAH, ajaha]
        System.out.println("ts = " + ts);
    }

    /*
        ​	1)数据唯一
        ​	2)可以对数据进行大小排序,
     */
    private static void method_1() {
        //自然排序 1)TreeSet()  构造一个新的空 set,默认排序方式,大小升序
        TreeSet<String> ts = new TreeSet<>();
        //添加数据
        ts.add("ajaha");
        ts.add("AJAH");
        ts.add("abc");
        ts.add("abcd");
        ts.add("abc");
        //输出 ts = [AJAH, abc, abcd, ajaha]
        System.out.println("ts = " + ts);
    }
}

小结:

1.TreeSet集合底层是红黑树数据结构,可以对其数据进行大小排序

2.TreeSet() 构造一个新的空 set,默认排序方式,大小升序

3.TreeSet(Comparator<? super E> comparator) 构造一个新的空 TreeSet,它根据指定比较器进行排序。
Comparator表示自定义排序接口,该接口下有一个排序方法:
int compare(T o1, T o2)
升序:o1 - o2
降序:o2 - o1

4.向TreeSet集合中存储自定义类的对象时只能使用带参数的构造方法:

TreeSet(Comparator<? super E> comparator) 传递比较器

5.综合练习(课下完成)

1.综合练习图解

在这里插入图片描述

2.综合练习中的实体类(javabean标准类)

在这里插入图片描述

父类Person:

package com.itheima.sh.domain;
/*
    父类
 */
public abstract class Person {
    //属性
    private int id;//编号
    private String name;//姓名
    private String sex;//性别
    private String birthday;//生日
    private int age;//年龄
    //构造方法

    public Person() {
    }

    public Person(int id, String name, String sex, String birthday, int age) {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.birthday = birthday;
        this.age = age;
    }

    public int getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", sex='" + sex + '\'' +
                ", birthday='" + birthday + '\'' +
                ", age=" + age +
                '}';
    }
    //类型
    public abstract String getType();
    //职业
    public abstract String getWork();
}

子类Student:

package com.itheima.sh.domain;

public class Student extends Person {
    //构造方法

    public Student() {
    }

    public Student(int id, String name, String sex, String birthday, int age) {
        super(id, name, sex, birthday, age);
    }
    //重写方法
    @Override
    public String getType() {
        return "学生";
    }

    @Override
    public String getWork() {
        return "学习Java";
    }

}

3.工具类

直接将资料中的Utils工具类复制到包下即可.

package com.itheima.sh.utils;

import com.itheima.sh.domain.Person;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;

public class Utils {
    public static int stuId = 0;//学员ID的初始值  后期可以改为从文件/数据库读取初始值
    public static int teaId = 0;//教师ID的初始值

    //通过生日计算年龄
    public static int birthdayToAge(String birthday) {//"1999-11-13"
        Date birthDate = null;
        //异常处理的代码,后边学


        //这个东西以后讲(idea生成就可以了)

            //创建日期格式化解析类对象指定时间格式,并使用parse方法将输入的字符串时间解析为Date
        try {
            birthDate =new SimpleDateFormat("yyyy-MM-dd").parse(birthday);
        } catch (ParseException e) {
            e.printStackTrace();
        }

        //获取当前系统时间
        Calendar now = Calendar.getInstance();
        //如果出生日期大于当前时间,则抛出异常
        Calendar birth = Calendar.getInstance();//获取当前日期的Calendar
	//将birthDate表示生日日期转换为Calendar  void setTime(Date date) 根据参数date日期更改对象birth的日期
        birth.setTime(birthDate);//将生日转换为Calendar(1999-11-13)

        //2020         2030
        // boolean before(Object when) 判断此 Calendar 表示的时间是否在指定 Object 表示的时间之  前,
	//返回判断结果。 
        //这里就是判断now表示的时间是否在参数birth的时间之前,如果是返回true
        //举例:当前时间:2020   出生日期填写2030 那么显然是非法的,那么before方法就会返回true
        //now : 2020   birth:2030 非法
        //now : 2020   birth:1990 合法

        if (now.before(birth)) {//判断当前日期是否在生日日期之前(before()的参数必须是一个Calendar类型)
            return -1;//表示计算失败
        }

        //取出系统当前时间的年、月、日部分
        int yearNow = now.get(Calendar.YEAR);//2020
        int monthNow = now.get(Calendar.MONTH);//5
        int day = now.get(Calendar.DAY_OF_MONTH);//17

        //取出出生日期的年、月、日部分
        int yearBirth = birth.get(Calendar.YEAR);//1999
        int monthBirth = birth.get(Calendar.MONTH);//11
        int dayBirth = birth.get(Calendar.DAY_OF_MONTH);//13

        //当前年份与出生年份相减,初步计算年龄
        //   21      2020       1999
        int age = yearNow - yearBirth;

        //当前月份与出生日期的月份相比,如果月份小于出生月份,则年龄上减1,表示不满多少周岁
        //   5           5
        if (monthNow < monthBirth) {
            age--;//20
        }

        //如果月份相等,在比较日期,如果当前日,小于出生日,也减1,表示不满多少周岁
        //   1999-3-20                    1999-3-30
        //2020-5-17      1999-5-17
        if (monthNow == monthBirth && day < dayBirth) {//如果:当前月 = 生日月
            age--;
        }

        return age;

    }

    //打印ArrayList的方法//  <? extends Person>是以后学的
    public static void printPersonList(ArrayList<? extends Person> list) {

        System.out.println("************************************************************************************");
        System.out.println("编号\t\t姓名\t\t性别\t\t生日\t\t\t\t年龄\t\t描述");
        //遍历集合
        for (int i = 0; i < list.size(); i++) {
            //获取集合的每个元素
            Person p = list.get(i);
            System.out.println(p.getId() + "\t\t" + p.getName() + "\t\t" + p.getSex() + "\t\t" + p.getBirthday() + "\t\t" + p.getAge() + "\t\t" +  "我是一名"+ p.getType() +"我要" + p.getWork());
        }
        System.out.println("************************************************************************************");

    }
}

4.一级菜单

package com.itheima.sh.run;

import com.itheima.sh.domain.Student;

import java.util.ArrayList;
import java.util.Scanner;

/*
    一级菜单
 */
public class MainApp {
    public static void main(String[] args) {
        //1.创建集合对象存储学生
        ArrayList<Student> list = new ArrayList<>();
        //创建键盘录入的对象
        Scanner sc = new Scanner(System.in);
//        ArrayList<Teacher> list2 = new ArrayList<>();
        //2.使用死循环控制一级菜单一直运行
        while (true) {
            System.out.println("***************************************");
            System.out.println("1.学员信息管理   2.教师信息管理   3.退出");
            System.out.println("***************************************");
            System.out.println("请输入菜单编号:");
            //获取输入菜单项
            int choose = sc.nextInt();
            //使用多分支结构
            switch (choose) {
                case 1:
                    //1.学员信息管理
                    StudentManager.manager(list);
                    break;
                case 2:
                    //2.教师信息管理
//                    TeacherManager.manager(list2);
                    break;
                case 3:
                    //3.退出 停止虚拟机
                    System.exit(0);
//                    return;//结束main方法
                default:
                    System.out.println("【您输入有误】");
                    break;
            }
        }
    }
}

5.二级菜单

public class StudentManager {
    //二级菜单 学员信息管理
    public static void manager(ArrayList<Student> list) {
        //死循环
        while (true) {
            System.out.println("=====================================================");
            System.out.println("【学员信息管理】");
            System.out.println("");//换行
            System.out.println("1.添加学员  2.修改学员  3.查询学员  4.删除学员  5.返回");
            System.out.println("=====================================================");
            //创建键盘录入的对象
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入菜单编号:");
            //获取编号
            int choose = sc.nextInt();
            //使用多分支根据录入的编号来操作学生管理系统
            switch (choose) {
                case 1:
                    //1.添加学员
                    break;
                case 2:
                    //2.修改学员
                    break;
                case 3:
                    //3.查询学员
                    break;
                case 4:
                    //4.删除学员
                    break;
                case 5:
                    //5.返回 到一级菜单,不是结束jvm虚拟机
                    //System.exit(0);一定不能使用这个代码
                    //结束方法manager
                    return;
                default:
                    System.out.println("【亲,您输入有误】");
            }
        }

    }
}

6.添加学生

 //添加学生
    private static void addStudent(ArrayList<Student> list) {
        //创建键盘录入的对象
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入学员姓名:");
        String name = sc.next();

        System.out.println("请输入学员性别:");
        String sex = sc.next();

        System.out.println("请输入学员出生日期:(2000-01-01)");
        String birthday = sc.next();

        //给编号+1
        Utils.stuId++;

        //计算年龄:根据生日birthday
        int age = Utils.birthdayToAge(birthday);
        //创建学生对象 Utils.stuId 获取上述加1的编号给学生编号
        Student s = new Student(Utils.stuId,name,sex,birthday,age);
        //将学生存储到集合list
        list.add(s);
        System.out.println("【添加成功】");
    }

7.查询学生

 //查询学生
    private static void findAllStudents(ArrayList<Student> list) {
       //对集合list进行判断是否有数据
        if(list.size() == 0){
            //说明集合没有数据
            System.out.println("【亲,没查到你要查的学生】");
        }else{
            //集合有数据
            //输出集合中的数据,使用工具类完成
            Utils.printPersonList(list);
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

娃娃 哈哈

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

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

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

打赏作者

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

抵扣说明:

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

余额充值