Java集合进阶——集合体系结构及各个集合的方法

        Java种的集合分为单列集合和双列集合,单列集合的最高层接口是Collection,双列集合的最高层是Map,这里先介绍单列集合

单列集合

体系结构:

注:红色框都为接口,蓝色框都为实现类(实现类Vector已经被淘汰)

Collection:

        Collection是一个接口,不能直接创建他的对象,所以学习他的方法时,只能创建他的实现类对象。例如ArrayList。

代码演示:
public class CollectionDemo {
    public static void main(String[] args) {
        /*
        public boolean add(E e)             添加
        public void clear()                 清空
        public boolean remove(E e)          删除
        public boolean contains(Object obj) 判断是否包含
        public boolean isEmpty()            判断是否为空
        public int size                     集合长度
         */

        Collection<String> coll = new ArrayList<>();

        //1.添加
        //注:添加方法会返回一个布尔类型的值
        //如果我们往List系列集合中添加数据,一定会返回true,因为List系列集合是允许元素重复的
        //如果往Set系列集合中添加数据,当元素不存在时才会返回true,否则返回false,因为Set集合时不允许重复的
        coll.add("aa");
        coll.add("bb");
        coll.add("cc");
        System.out.println(coll);

        //2.清空
        coll.clear();
        System.out.println(coll);
        //再添加回来
        coll.add("aa");
        coll.add("bb");
        coll.add("cc");

        //3.删除
        coll.remove("bb");
        System.out.println(coll);

        //4.判断是否包含
        boolean result1 = coll.contains("bb");
        System.out.println(result1);

        //5.判断是否为空
        boolean result2 = coll.isEmpty();
        System.out.println(result2);

        //6.集合长度
        int size = coll.size();
        System.out.println(size);
    }
}
运行结果:

(1)contains方法的拓展:

        如果让contains方法判断自定义的类对象,需要重写equals方法才能得到正确结果,因为contains方法底层就调用了equals方法,如果没有重写,默认使用的就是Object类中的equals方法,比较的是地址值。

没有重写equals时
代码演示:
学生类Student:
public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
测试类Test:
public class CollectionContainsDemo {
    public static void main(String[] args) {
        Collection<Student> coll = new ArrayList<>();
        Student s1 = new Student("xiaobai",21);
        Student s2 = new Student("xiaohei",22);
        Student s3 = new Student("xiaohei", 22);//与上面s2一样
        coll.add(s1);
        coll.add(s2);
        boolean result = coll.contains(s3);
        System.out.println(result);
    }
}
运行结果:

重写equals时
代码演示:
Student类:

加入下面这段代码

@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);
    }

        测试类不改变

运行结果:

(2)Collection系列集合三种通用的遍历方式:

        ①迭代器遍历

        ②增强for遍历

        ③lambda表达式遍历

接下来进行代码演示:

①迭代器遍历:

迭代器遍历相关的三个方法:

        Iterator<E> iterator()        :        获取一个迭代器对象

        boolean hasNext()           :        判断当前指向的位置是否有元素

        E next()                            :        获取当前指向的元素并移动指针

代码演示:
public class ErgodicDemo {
    public static void main(String[] args) {
        //迭代器遍历相关的三个方法:
        //Iterator<E> iterator():获取一个迭代器对象
        //boolean hasNext(): 判断当前指向的位置是否有元素
        //E next():获取当前指向的元素并移动指针

        //创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aa");
        coll.add("bb");
        coll.add("cc");

        //获取迭代器对象
        Iterator<String> it = coll.iterator();
        while(it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }
    }
}
运行结果:

迭代器注意点:

        1.指针指向没有元素的位置时再强行调用next方法,会报错NoSuchElementException

        2.迭代器遍历完毕,指针不会复位

        3.循环中只能用一次next方法

        4.迭代器遍历时,不能用集合的方法进行增加或者删除(可以使用迭代器的remove方法删除)

②增强for遍历:

        格式:

                for(数据类型 变量名 : 集合/数组){

                        方法体

                }

        快速生成方式:集合名字.for 再按回车

代码演示:
public class EnhancedFor {
    public static void main(String[] args) {
        //创建集合并存储元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aa");
        coll.add("bb");
        coll.add("cc");
        //增强for遍历
        //循环遍历中,下面的第三方变量s会依次表示集合中的每一个元素
        for(String s : coll) {
            System.out.println(s);
        }
        
    }
}
运行结果:

增强for注意点:

        修改增强for中的变量,不会改变集合中原本的数据。因为增强for中使用了参数中设置的第三方变量存储,修改的是第三方变量的值。

③Lambda表达式:
代码演示:
public class LambdaDemo {
    public static void main(String[] args) {
        //default void forEach(Consumer<? super T> action):

        //1.创建集合并添加元素
        Collection<String> coll = new ArrayList<>();
        coll.add("aa");
        coll.add("bb");
        coll.add("cc");

        //2.使用匿名内部类遍历
        coll.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

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

        //3.使用Lambda表达式遍历
        coll.forEach(s -> System.out.println(s));
    }
}
运行结果:

④三种遍历方式适用情况:

        若在遍历过程中需要删除元素,则使用迭代器

        若仅仅想遍历,则使用增强for或Lambda表达式

1.List

        List系列集合特点:有序,可重复,有索引

        虽然Collection的方法List都继承了,但List集合因为有索引,所以多了很多索引操作的方法,下面介绍一下List系列集合独有的方法。

List系列集合独有的方法

代码演示:
public class ListDemo {
    public static void main(String[] args) {
        /*
        List系列集合独有的方法:
            void add(int index,E element)   在此集合中的指定位置插入指定的元素
            E remove(int index)             删除指定索引处的元素,返回被删除的元素
            E set(int index,E element)      修改指定索引处的元素,返回被修改的元素
            E get(int index)                返回指定索引处的元素
        */

        //1.创建一个集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");
        //2.void add(int index,E element)   在此集合中的指定位置插入指定的元素
        list.add(1,"ee");
        System.out.println(list);
        System.out.println("----------------");
        //3.E remove(int index)   删除指定索引处的元素,返回被删除的元素
        /*remove注意点:当集合中存储的是int类型的数据,删除时输入int类型整数,
        会首先认为是删除索引,但是输入一个Integer类型的变量,则会认为是删除元素*/
        String remove = list.remove(1);
        System.out.println("被删除的元素:" + remove);
        System.out.println(list);
        System.out.println("----------------");
        //4.E set(int index,E element)  修改指定索引处的元素,返回被修改的元素
        String result = list.set(1, "ee");
        System.out.println("被修改的元素:" + result);
        System.out.println(list);
        System.out.println("----------------");
        //5.E get(int index)    返回指定索引处的元素
        System.out.println(list.get(0));

    }
}
运行结果:

List集合的遍历方式

        List继承了Collection,所以Collection中的遍历方式在List中都可以使用,即:

  ①迭代器遍历
  ②增强for遍历
  ③Lambda表达式遍历

此外List系列集合还有自己的遍历方式,即:

  ④普通for遍历
     代码演示:
public class OrdinaryFor {
    public static void main(String[] args) {
        //创建集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");

        //遍历
        for (int i = 0; i < list.size(); i++) {
            String str = list.get(i);
            System.out.println(str);
        }
    }
}
运行结果:

⑤列表迭代器遍历

        它可以在遍历时添加和删除元素

    代码演示:
public class ListIteraterDemo {
    public static void main(String[] args) {
        //创建集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aa");
        list.add("bb");
        list.add("cc");

        //创建列表迭代器
        ListIterator<String> it1 = list.listIterator();

        //遍历
        while(it1.hasNext()) {
            String str = it1.next();
            System.out.println(str);
        }

        //它可以在遍历中添加或删除元素
        ListIterator<String> it2 = list.listIterator();
        while(it2.hasNext()) {
            String str = it2.next();
            if("aa".equals(str)) {
                it2.add("ee");
            }
            if("cc".equals(str)) {
                it2.remove();
            }
        }
        System.out.println(list);
    }
}
运行结果:

五种遍历方式总结:

        在遍历过程中需要删除元素时,使用迭代器

        在遍历过程中需要添加元素时,使用列表迭代器

        如果仅仅想遍历,使用增强for或者Lambda表达式

        如果遍历时想操作索引,可以使用普通for

(1)ArrayList

        ArrayList底层是数组结构

底层原理:

        ①利用空参创建的集合,在底层创建一个默认长度为0的数组

        ②添加第一个元素时,底层会创建一个新的长度为10的数组

        如图(elementDate是数组名,size是指针)

        ③存满时,会扩容1.5倍(即第一次存满后数组大小扩容到15)

        ④如果一次添加多个元素,扩容到1.5倍放不下,则新建数组的长度以实际为准(即一开始十             个数据存满数组的情况下,再一次性添加100个元素,那么新数组长度就为110)

 迭代器底层原理图解:

(2)LinkedList

        LinkedList底层数据结构是双向链表,查询慢,增删快,但是如果操作的是首尾元素,速度也是极快的

双向链表模型:

        由于上述特点,LinkedList中多了很多首尾操作的特有API,例如:

public void addFirst(E e)在该列表开头插入指定的元素
public void addLast(E e)将指定的元素追加到此列表的末尾
public E getFirst()返回此列表中的第一个元素
public E getLast()返回此列表中的最后一个元素
public E removeFirst()从此列表中删除并返回第一个元素
public E removeLast()从此列表中删除并返回最后一个元素
添加元素原理图解:

2.Set

        Set系列集合特点:无序,不可重复,无索引

Set中的方法:

        Set是一个接口,这个接口中的方法基本上与Collection中的API一致,方法如下

        public boolean add(E e)             添加
        public void clear()                 清空
        public boolean remove(E e)          删除
        public boolean contains(Object obj) 判断是否包含
        public boolean isEmpty()            判断是否为空
        public int size                     集合长度

        这部分方法在前文Collection模块中已经演示,这里不再演示。

Set的遍历方法:

        也可以使用Collection中的遍历方法,如:

        ①迭代器遍历

        ②增强for遍历

        ③Lambda表达式遍历

        这三种方法前文已经演示,注意因为Set系列集合没有索引,所以不能使用普通for遍历

(1)HashSet

特点:

        无序,不可重复,无索引

HashSet底层原理概要:

        HashSet集合底层采取哈希表存储数据

        哈希表是一种对于增删查改数据性能都较好的结构

接下来我们先介绍一下哈希表

哈希表:
        组成:

                JDK8之前:数组 + 链表

                JDK8开始:数组 + 链表 + 红黑树

        哈希表中有一个非常重要的值:哈希值

        哈希值:

                可以理解为对象的整数表现形式

                ·根据hashCode方法算出来的int类型的整数

                ·该方法定义在Object类中,所有对象都可以调用,默认使用地址值进行计算

                ·一般情况下,会重写hashCode方法,利用对象内部的属性值计算哈希值

        对象哈希值的特点:

                ·如果没有重写hashCode方法,不同对象计算出的哈希值是不同的

                ·如果已经重写hashCode方法,不同的对象只要属性值相同,计算出的哈希值也一样

                ·在小部分情况下,不同的属性值或者地址值计算出来的哈希值有可能一样(哈希碰撞)

       
未重写hashCode情况代码演示

        编写学生类来创建对象

        Student:

public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

        编写测试类:

        HashSetDemo1:

public class HashSetDemo1 {
    public static void main(String[] args) {
        //创建两个属性一样的学生对象
        Student s1 = new Student("xiaobai", 21);
        Student s2 = new Student("xiaobai", 21);

        System.out.println(s1.hashCode());
        System.out.println(s2.hashCode());
    }
}
运行结果

未重写hashCode情况代码演示

        在Student类中加入了重写的hashCode方法

        Student:

public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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



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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}

        HashSetDemo1中的代码不变

运行结果

HashSet的底层原理

注意:

        1.如果元素应存入位置已经有多个元素形成链表,需要调用equals方法比较每个元素,若都不一样,则挂在链表最后。

        2.默认加载因子:默认加载因子为0.75表示当数组里面存了16 * 0.75 = 12个元素时,数组就会扩容成原来的两倍。

        3.当链表长度大于8而且数组长度大于等于64时,这个链表将会自动转成红黑树,从而提高查找效率。

        4.如果集合中存储的是自定义对象,必须要重写hashCode和equals方法。

HashSet小练习:

需求:创建一个存储学生对象的集合,存储多个学生对象 使用程序实现在控制台遍历该集合
要求:学生对象的成员变量值相同,我们就认为是同一个对象

代码演示:
Student类:
public class Student {
    private String name;
    private int age;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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


    @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);
    }

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }
}
测试类HashSetTest1:
public class HashSetTest1 {
    public static void main(String[] args) {
        /*
        需求:创建一个存储学生对象的集合,存储多个学生对象
        使用程序实现在控制台遍历该集合
        要求:学生对象的成员变量值相同,我们就认为是同一个对象
         */

        HashSet<Student> hashSet = new HashSet<>();

        //创建学习对象    三个xiaobai 两个xiaohei 一个xiaohuang
        Student s1 = new Student("xiaobai",21);
        Student s2 = new Student("xiaobai",21);
        Student s3 = new Student("xiaobai",21);
        Student s4 = new Student("xiaohei",20);
        Student s5 = new Student("xiaohei",20);
        Student s6 = new Student("xiaohuang",20);

        //添加到集合中
        hashSet.add(s1);
        hashSet.add(s2);
        hashSet.add(s3);
        hashSet.add(s4);
        hashSet.add(s5);
        hashSet.add(s6);

        //遍历
        Iterator<Student> it = hashSet.iterator();
        while(it.hasNext()) {
            Student str = it.next();
            System.out.println(str);
        }
    }
}
运行结果:

注意:

        如果未在Student类中重写hashCode和equals方法,属性值相等的对象也是会存入集合的。

LinkedHashSet:
特点:

        LinkedHashSet是有序(存储和取出的元素顺序一致),无重复,无索引的

代码演示:
public class LinkedHashSetDemo1 {
    public static void main(String[] args) {
        LinkedHashSet<Student> lhs = new LinkedHashSet<>();

        Student s1 = new Student("xiaobai", 20);
        Student s2 = new Student("xiaohei", 21);
        Student s3 = new Student("xiaohuang", 22);
        Student s4 = new Student("xiaohei", 21);

        lhs.add(s1);
        lhs.add(s2);
        lhs.add(s3);
        lhs.add(s4);

        System.out.println(lhs);
    }

}
运行结果:
原理:

        底层数据结构依然是哈希表,只是每个元素又额外多了一个双链表的机制记录存储的顺序

        如下图红色双线表示双链表存储

适用情况:

        如果只需要数据去重,那么默认使用HashSet

        如果要求去重且存取有序,就使用LinkedHashSet

(2)TreeSet
特点:

        不重复,无索引,可排序(按照元素的默认规则从小到大排序)

        TreeSet集合底层是基于红黑树的数据结构(不了解红黑树可以查看作者的另一篇文章《Java基合进阶——数据结构》)实现排序的,增删改查性能都较好

代码演示:
public class TreeSetDemo1 {
    public static void main(String[] args) {
        TreeSet<Integer> ts = new TreeSet<>();

        ts.add(3);
        ts.add(5);
        ts.add(2);
        ts.add(1);
        ts.add(4);

        System.out.println(ts);
    }
}
运行结果:

若使用Collection中的那三种遍历方式遍历,也会得到这个顺序的结果

排序规则:

        对于数值类型:Integer,Double,默认按照从小到大的顺序进行排序

        对于字符、字符串类型:按照字符在ASCII码表中的数字升序进行排序

        (字符串中从第一个字母挨个比较,并且若前若干个字母一样,后面没有字母的那个字符串排在后边还有字母的字符串前面,例如:aaa,ab,aba 这样排序)

TreeSet的两种比较方式

        TreeSet有两种比较方式,为默认排序/自然排序和比较器排序

使用原则:

        默认使用第一种,如果第一种不能满足当前需求(比如字符串中默认的规则需要修改,或者Integer默认排序规则为从小到大若不是用这个规则则使用第二种),就使用第二种

1.默认排序/自然排序:

        对数据类型可直接使用,对自定义对象使用需要先在自定义的JavaBean中实现Comparable接口,并重写其中的compareTo方法,在这个方法中可以定义根据什么排序。

下面举例一个重写后的compareTo方法,它的作用是只看年龄,按照年龄的升序进行排列

@Override
public int compareTo(Student o){
    return this.getAge() - o.getAge();
}

this:表示当前要添加的元素        o:表示已经在红黑树存在的元素

返回值:

        负数:认为要添加的元素是小的,存左边

        正数:认为要添加的元素是大的,存右边

        0     :认为要添加的元素已经存在,舍弃

当添加某元素后TreeSet的数据结构不满足红黑树的规则了,它就会自己通过调整节点颜色或左旋右旋调整来满足红黑树的规则(想要详细了解可以看作者的另一篇文章《Java集合进阶——数据结构》)

2.比较器排序

        创建TreeSet对象时候,传递比较器Comparator指定规则

比较器排序小练习1:

        需求:存入四个字符串:"c","ab","df","qwer"

        按照长度排序,如果一样长则按照首字母排序

代码演示:
public class TreeSetTest1 {
    public static void main(String[] args) {
        //需求:存入四个字符串:"c","ab","df","qwer"
        //按照长度排序,如果一样长则按照首字母排序

        //创建TreeSet对象
        TreeSet<String> ts = new TreeSet<>(new Comparator<String>() {
            //o1为要添加的元素
            //o2为已经在红黑树中存在的元素
            //返回值的规则跟之前一样
            @Override
            public int compare(String o1, String o2) {
                //根据长度排序
                int i = o1.length() - o2.length();

                //根据字母排序
                //如果i == 0即长度相等,那么就使用默认的比较规则,按字母排序,
                //如果长度不相等,那么直接根据长度排序
                i = i == 0 ? o1.compareTo(o2) : i;
                return i;
            }
        });

        //添加元素
        ts.add("c");
        ts.add("ad");
        ts.add("df");
        ts.add("qwer");

        //输出
        System.out.println(ts);

    }

}
运行结果:

 比较器排序小练习2:

        需求:创建5个学生对象

        属性:姓名,年龄,语文成绩,数学成绩,英语成绩

        按照总分从高到低输出到控制台

        如果总分一样,按照语文成绩排

        如果语文成绩一样,按照数学成绩排

        如果数学成绩一样,按照英语成绩排

        如果英语成绩一样,按照年龄排

        如果年龄一样,按照姓名的字母顺序排

        如果都一样,认为是同一个学生,不存

代码演示:

Student2:

public class Student2 implements Comparable<Student2>{
    private String name;
    private int age;
    private int chineseGrade;
    private int mathGrade;
    private int englishGrade;

    public Student2() {
    }

    public Student2(String name, int age, int chineseGrade, int mathGrade, int englishGrade) {
        this.name = name;
        this.age = age;
        this.chineseGrade = chineseGrade;
        this.mathGrade = mathGrade;
        this.englishGrade = englishGrade;
    }

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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

    /**
     * 获取
     * @return chineseGrade
     */
    public int getChineseGrade() {
        return chineseGrade;
    }

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

    /**
     * 获取
     * @return mathGrade
     */
    public int getMathGrade() {
        return mathGrade;
    }

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

    /**
     * 获取
     * @return englishGrade
     */
    public int getEnglishGrade() {
        return englishGrade;
    }

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



    public String toString() {
        int sum = chineseGrade + mathGrade + englishGrade;
        return "Student2{name = " + name + ", age = " + age + ", chineseGrade = " + chineseGrade + ", mathGrade = " + mathGrade + ", englishGrade = " + englishGrade + ", sum = " + sum + "}";
    }

    @Override
    public int compareTo(Student2 o) {
        int score1 = this.getChineseGrade() + this.getMathGrade() + this.getEnglishGrade();
        int score2 = o.getChineseGrade() + o.getMathGrade() + o.getEnglishGrade();
        int i = score1 - score2;
        i = i == 0 ? this.getChineseGrade() - o.getChineseGrade() : i;
        i = i == 0 ? this.getMathGrade() - o.getMathGrade() : i;
        i = i == 0 ? this.getEnglishGrade() - o.getEnglishGrade() : i;
        i = i == 0 ? this.getAge() - o.getAge() : i;
        i = i == 0 ? this.getName().compareTo(o.getName()) : i;
        return i;
    }
}

测试类TreeSetTest2:

public class TreeSetTest2 {
    public static void main(String[] args) {
        //需求:创建5个学生对象
        //属性:姓名,年龄,语文成绩,数学成绩,英语成绩
        //按照总分从高到低输出到控制台
        //如果总分一样,按照语文成绩排
        //如果语文成绩一样,按照数学成绩排
        //如果数学成绩一样,按照英语成绩排
        //如果英语成绩一样,按照年龄排
        //如果年龄一样,按照姓名的字母顺序排
        //如果都一样,认为是同一个学生,不存

        //创建集合并定义排序规则
        TreeSet<Student2> ts = new TreeSet<>();

        //创建对象
        Student2 s1 = new Student2("laoda",20,65,66,67);
        Student2 s2 = new Student2("laoer",21,67,66,67);
        Student2 s3 = new Student2("laosan",22,65,66,67);
        Student2 s4 = new Student2("laosi",21,65,68,67);
        Student2 s5 = new Student2("laowu",22,65,80,69);

        //添加对象
        ts.add(s1);
        ts.add(s2);
        ts.add(s3);
        ts.add(s4);
        ts.add(s5);

        for (Student2 stu : ts) {
            System.out.println(stu);
        }

    }
}
运行结果:

集合工具类Collections

常用API:
public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素
public static void shuffle(List<?> list)                        打乱List集合元素的顺序
public static <T> void sort(List<T> list, T key)                排序
public static <T> void sort(List<T> list, Comparator<T> c)      根据指定的规则进行排序
public static <T> int binarySearch(List<T> list, T key)         以二分查找法查找元素
public static <T> void copy(List<T> dest, List<T> src)          拷贝集合中的元素
public static <T> int fill(List<T> list, T obj)                 使用指定的元素填充集合
public static <T> void max/min(Collection<T> coll)              根据默认的自然排序获取最                                                                       大/最小值
public static <T> void swap(List<?> list, int i, int j)         交换集合中指定位置的元素
代码演示:
public class CollectionsDemo1 {
    public static void main(String[] args){
        /*
        public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素
        public static void shuffle(List<?> list)                        打乱List集合元素的顺序
        public static <T> void sort(List<T> list, T key)                排序
        public static <T> void sort(List<T> list, Comparator<T> c)      根据指定的规则进行排序
        public static <T> int binarySearch(List<T> list, T key)         以二分查找法查找元素
        public static <T> void copy(List<T> dest, List<T> src)          拷贝集合中的元素
        public static <T> int fill(List<T> list, T obj)                 使用指定的元素填充集合
        public static <T> void max/min(Collection<T> coll)              根据默认的自然排序获取最大/最小值
        public static <T> void swap(List<?> list, int i, int j)         交换集合中指定位置的元素
         */

        //创建集合
        ArrayList<String> list = new ArrayList<>();

        //1.public static <T> boolean addAll(Collection<T> c, T...elements) 批量添加元素
        System.out.println("----------批量添加元素----------");
        //注意只能添加Collection下的类
        Collections.addAll(list,"aaa","bbb","ccc","123");
        System.out.println(list);

        //2.public static void shuffle(List<?> list)                        打乱List集合元素的顺序
        System.out.println("----------打乱List集合元素的顺序----------");
        Collections.shuffle(list);
        System.out.println(list);

        //3.public static <T> void sort(List<T> list, T key)                以二分查找法查找元素
        System.out.println("----------排序----------");
        Collections.sort(list);
        System.out.println(list);


        //4.public static <T> void sort(List<T> list, Comparator<T> c)      根据指定的规则进行排序
        System.out.println("----------根据指定的规则进行排序----------");
        Collections.sort(list, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                //按默认字符串比较规则排序
                return o1.compareTo(o2);
            }
        });
        System.out.println(list);

        //5.public static <T> int binarySearch(List<T> list, T key)         以二分查找法查找元素
        System.out.println("----------以二分查找法查找元素----------");
        System.out.println(list);
        int i = Collections.binarySearch(list, "bbb");
        System.out.println(i);

        //6.public static <T> void copy(List<T> dest, List<T> src)          拷贝集合中的元素
        System.out.println("----------拷贝集合中的元素----------");
        //新建一个集合
        ArrayList<String> listCopy = new ArrayList<>();
        //注意被拷贝的集合长度要与拷贝集合长度一致
        Collections.addAll(listCopy,"","","","");
        Collections.copy(listCopy,list);
        System.out.println(listCopy);

        //7.public static <T> int fill(List<T> list, T obj)                 使用指定的元素填充集合
        System.out.println("----------使用指定的元素填充集合----------");
        Collections.fill(list,"666");
        System.out.println(list);

        //8.public static <T> void max/min(Collection<T> coll)              根据默认的自然排序获取最大/最小值
        System.out.println("----------根据默认的自然排序获取最大/最小值----------");
        String max = Collections.max(listCopy);
        String min = Collections.min(listCopy);
        System.out.println(max);
        System.out.println(min);

        //9.public static <T> void swap(List<?> list, int i, int j)         交换集合中指定位置的元素
        System.out.println("----------交换集合中指定位置的元素----------");
        System.out.println(listCopy);
        Collections.swap(listCopy,1,2);
        System.out.println(listCopy);


    }
}
运行结果:

单列集合使用场景

1.ArrayList

        如果想要集合中的元素可重复,(用的最多)用ArrayLst集合,基于数组的。

2.LinkedList

        如果想要集合中的元素可重复,而且当前的增删操作明显多于查询,用LinkedList,基于链表的

3.HashSet

        如果想对集合中的元素去重,(用的最多)用HashSet,基于哈希表的

4.LinkedHashSet

        如果相对集合中的元素去重,而且保证存取顺序,用LinkedHashSet,基于哈希表和双链表,效率低于HashSet

5.TreeSet

        如果相对集合中的元素进行排序,用TreeSet,基于红黑树。后续也可使用List集合实现排序

双列集合

介绍:

        双列集合每次添加添加两个(或者说一对)元素

        如图:

        左边这列称为键,不可以重复,右边这列称为值,可以重复,键跟值是一一对应的,每一个键只能找到自己的值。

        一对一一对应的键和值统称为一个键值对或者键值对对象,Java中也叫Entry对象。

体系结构

        由于Hashtable和Properties与IO有关,所以先不讲解

MAP

常用API

V put(K key,V value)                    添加/覆盖元素
V remove(Object key)                    根据键删除键值对元素
void clear()                            移除所有的键值对元素
boolean containsKey(Object key)         判断集合是否包含指定的键
boolean containsValue(Object value)     判断集合是否包含指定的值
boolean isEmpty()                       判断集合是否为空
int size()                              集合的长度,即集合中键值对的个数
        代码演示:
public class MapDemo1 {
    public static void main(String[] args) {
        /*
        V put(K key,V value)                    添加/覆盖元素
        V remove(Object key)                    根据键删除键值对元素
        void clear()                            移除所有的键值对元素
        boolean containsKey(Object key)         判断集合是否包含指定的键
        boolean containsValue(Object value)     判断集合是否包含指定的值
        boolean isEmpty()                       判断集合是否为空
        int size()                              集合的长度,即集合中键值对的个数
         */

        //创建集合对象
        //因为Map为接口,所以利用HashMap创建对象
        Map<String,String> m1 = new HashMap<>();

        //添加/覆盖元素
        System.out.println("----------添加/覆盖元素----------");
        //若要添加的键不存在,则直接添加到集合中并返回null
        //若要添加的键存在,则将原来的键值对对象覆盖,并返回被覆盖的值
        String result1 = m1.put("灰太狼", "红太狼");
        m1.put("美羊羊","沸羊羊");
        m1.put("懒羊羊","小灰灰");
        System.out.println(m1);
        System.out.println(result1);
        //新添加一个已经存在的键
        String result2 = m1.put("美羊羊","喜羊羊");
        System.out.println(result2);
        System.out.println(m1);

        //根据键删除键值对元素
        System.out.println("----------根据键删除键值对元素----------");
        String result3 = m1.remove("灰太狼");
        System.out.println(result3);
        System.out.println(m1);

        //移除所有的键值对元素
        System.out.println("----------移除所有的键值对元素----------");
        m1.clear();
        System.out.println(m1);

        //重新添加元素
        m1.put("灰太狼", "红太狼");
        m1.put("懒羊羊","小灰灰");
        m1.put("美羊羊","喜羊羊");

        //判断集合是否包含指定的键
        System.out.println("----------判断集合是否包含指定的键----------");
        boolean result4 = m1.containsKey("懒羊羊");
        boolean result5 = m1.containsKey("烤全羊");
        System.out.println(result4);
        System.out.println(result5);

        //判断集合是否包含指定的值
        System.out.println("----------判断集合是否包含指定的值----------");
        boolean result6 = m1.containsValue("小灰灰");
        boolean result7 = m1.containsValue("烤全羊");
        System.out.println(result6);
        System.out.println(result7);

        //判断集合是否为空
        System.out.println("----------判断集合是否为空----------");
        boolean result8 = m1.isEmpty();
        System.out.println(result8);

        //集合的长度,即集合中键值对的个数
        System.out.println("----------集合的长度,即集合中键值对的个数----------");
        int size = m1.size();
        System.out.println(size);

    }
}
运行结果:

三种遍历方式:

1.通过键找值遍历

        下面我来使用键找值遍历方式,通过迭代器、增强for、Lambda表达式遍历集合。

        代码演示:
public class MapDemo2 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String,String> m = new HashMap<>();

        //添加对象
        m.put("喜羊羊","灰太狼");
        m.put("沸羊羊","美羊羊");
        m.put("懒羊羊","小灰灰");

        //通过键找值遍历
        //将双列集合中的键提另出来放入一个单列集合
        Set<String> keys = m.keySet();

        //1.迭代器
        System.out.println("----------迭代器----------");
        //创建迭代器对象
        Iterator<String> it = keys.iterator();
        //使用迭代器遍历
        while(it.hasNext()) {
            String key = it.next();
            //键找值
            String value = m.get(key);
            System.out.println(key + " = " + value);
        }

        //2.增强for
        System.out.println("----------增强for----------");
        for (String key : keys) {
            String value = m.get(key);
            System.out.println(key + " = " + value);
        }

        //3.Lambda表达式
        System.out.println("----------Lambda表达式----------");
        keys.forEach(key -> {
                String value = m.get(key);
                System.out.println(key + " = " + value);
            }
        );
        

    }
}
运行结果:

        2.通过键值对遍历

        下面我来使用键值对遍历方式,通过迭代器、增强for、Lambda表达式遍历集合。

        代码演示
public class MapDemo3 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String,String> m = new HashMap<>();

        //添加元素
        m.put("喜羊羊","灰太狼");
        m.put("沸羊羊","美羊羊");
        m.put("懒羊羊","小灰灰");

        //通过键值对遍历
        //将键值对对象放到一个单列集合中
        Set<Map.Entry<String, String>> entries = m.entrySet();

        //1.迭代器
        System.out.println("----------增强for----------");
        Iterator<Map.Entry<String, String>> it = entries.iterator();
        while(it.hasNext()) {
            Map.Entry<String, String> entry = it.next();
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + " = " + value);
        }

        //2.增强for
        System.out.println("----------增强for----------");
        for (Map.Entry<String, String> entry : entries) {
            String key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + " = " + value);
        }

        //3.Lambda表达式
        System.out.println("----------Lambda表达式----------");
        entries.forEach(entry -> {
                String key = entry.getKey();
                String value = entry.getValue();
                System.out.println(key + " = " + value);
            }
        );

    }
}
运行结果:

        3.Lambda表达式

   default void forEach(BiConsumer<? super K, ? super V> action)        结合Lambda遍历Map集合

        代码演示:
public class MapDemo4 {
    public static void main(String[] args) {
        //创建集合对象
        Map<String,String> m = new HashMap<>();

        //添加元素
        m.put("喜羊羊","灰太狼");
        m.put("沸羊羊","美羊羊");
        m.put("懒羊羊","小灰灰");

        //使用Lambda表达式遍历
        //1.匿名内部类
        m.forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key + " = " + value);
            }
        });
        System.out.println("---------------------");
        
        //2.转换成Lambda表达式
        m.forEach((key, value) -> System.out.println(key + " = " + value)
        );
    }
}
        运行结果:

1.HashMap

        特点:

                1.HashMap是Map里面的一个实现类

                2.没有额外需要学习的特有方法,直接使用Map里面的方法就可以了

                3.特点都是由键决定的:无序、不重复、无索引(都是指键)

                4.HashMap跟HashSet底层原理是一模一样的,都是哈希表结构(JDK8之前:数组+链                     表,JDK8之后:数组+链表+红黑树)计算哈希值时也是通过键计算

                5.存入元素时,以hashCode方法和equals方法来保证键的唯一

                6.如果键存储的是自定义对象,需要重写hashCode和equals方法,如果值存储自定义对                     象,不需要重写hashCode和equals方法

小练习1

需求:创建一个HashMap集合,
键是学生对象(Student),值是籍贯(String)
存储三个键值对元素,并遍历
要求:同姓名、同年龄认为是同一个学生

核心点:HashMap的键位置如果存储的是自定义对象,需要重写hashCode和equals方法。

代码演示:
public class HashMapTest1 {
    public static void main(String[] args) {
        /*
        需求:创建一个HashMap集合,
        键是学生对象(Student),值是籍贯(String)
        存储三个键值对元素,并遍历
        要求:同姓名、同年龄认为是同一个学生
         */

        //创建学生对象
        Student s1 = new Student("han",21);
        Student s2 = new Student("ma",22);
        Student s3 = new Student("zhao",21);
        Student s4 = new Student("zhao",21);

        //创建HashMap集合
        HashMap<Student,String> hm = new HashMap<>();

        //存储键值对元素
        hm.put(s1,"石家庄");
        hm.put(s2,"邯郸");
        hm.put(s3,"保定");
        hm.put(s4,"天津");

        //遍历
        //1.键找值遍历
        System.out.println("----------键找值遍历----------");
        Set<Student> keys = hm.keySet();
        for (Student key : keys) {
            String value = hm.get(key);
            System.out.println(key + " = " + value);
        }

        //2.键值对遍历
        System.out.println("----------键值对遍历----------");
        Set<Map.Entry<Student, String>> entries = hm.entrySet();
        for (Map.Entry<Student, String> entry : entries) {
            Student key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key + " = " + value);
        }


        //3.Lambda表达式遍历
        System.out.println("----------Lambda表达式遍历----------");
        hm.forEach((student,  s) ->System.out.println(student + " = " + s));

    }
}
运行结果:

小练习2

        需求:某个班级30名学生,现在需要组成秋游活动,班长提供了四个景点依次是A、B、C、D,每个学生只能选择一个景点,请统计出最终哪个景点想去的人数最多

代码演示
public class HashMapTest2 {
    public static void main(String[] args) {
        /*
    需求:某个班级30名学生,现在需要组成秋游活动,
    班长提供了四个景点依次是A、B、C、D,
    每个学生只能选择一个景点,
    请统计出最终哪个景点想去的人数最多
     */

        //创建集合
        HashMap<String,Integer> hm = new HashMap<>();

        //生成三十个随机的景点表示学生选择的景点,并存入集合
        ArrayList<String> list = new ArrayList<>();
        String[] arr = {"A","B","C","D"};
        Random r = new Random();
        for (int i = 0; i < 30; i++) {
            int index = r.nextInt(4);
            String name = arr[index];
            list.add(name);
        }

        //要统计的东西较多,使用Map集合进行统计
        for (int i = 0; i < list.size(); i++) {
            //判断当前的元素在HashMap中是否存在
            if(hm.containsKey(list.get(i))) {
                //如果存在,这个key的value加1
                int newValue = hm.get(list.get(i)) + 1;
                hm.put(list.get(i),newValue);
            } else {
                //如果不存在,那么添加进去value置为1
                hm.put(list.get(i),1);
            }
        }

        //遍历
        System.out.println(hm);

        //找到选择次数的最大值
        int max = 0;
        Set<Map.Entry<String, Integer>> entries = hm.entrySet();
        for (Map.Entry<String, Integer> entry : entries) {
            int times = entry.getValue();
            if(max < times) {
                max = times;
            }
        }

        //输出次数等于最大次数的景点
        System.out.print("想去的人最多的景点为:");
        for (Map.Entry<String, Integer> entry : entries) {
            int times = entry.getValue();
            if(max == times) {
                System.out.print(entry.getKey() + " ");
            }
        }


    }

}
运行结果:

2.LinkedHashMap

        特点:

                由键决定:有序、无重复、无索引

                这里的有序指的是保证存储和取出的元素顺序一致

        原理:

                底层数据结构依然是哈希表,只是每个键值对元素又额外多了一个双链表的机制记录存储的顺序(详细了解可通过前文LinkedHashSet内容)

3.TreeMap

        特点:

                由键决定:不重复、无索引、可排序

                这里的可排序是指对键进行排序(默认按照键的大小从小到大进行排序,也可以自己规定键的排序规则)

        原理:

                与TreeSet一样,底层数据结构都为红黑树,增删改查性能都较好

        代码书写两种排序规则(同TreeSet):

                1.实现Comparable接口,指定比较规则。

                2.创建集合时传递Comparator比较器对象,指定比较规则。

        小练习1:        

                需求:

                键:整数表示id

                值:字符串表示商品名称

                要求1:按照id的升序排列(默认情况下TreeMap对Integer类型的键值就从小到大排序)

                要求2:按照id的降序排列

        代码演示:
public class TreeMapDemo1 {
    public static void main(String[] args) {
        /*
        需求:
        键:整数表示id
        值:字符串表示商品名称
        要求1:按照id的升序排列
        要求2:按照id的降序排列
         */

        //要求1:升序
        //需要排序,创建TreeMap对象
        TreeMap<Integer,String> tm1 = new TreeMap<>((o1, o2) -> o1 - o2);

        //添加元素
        tm1.put(2,"phone");
        tm1.put(3,"computer");
        tm1.put(1,"pen");

        //输出结果
        System.out.println(tm1);


        //要求2:降序
        //创建TreeMap对象
        TreeMap<Integer,String> tm2 = new TreeMap<>((o1, o2) -> o2 - o1);

        //添加元素
        tm2.put(1,"pen");
        tm2.put(3,"computer");
        tm2.put(2,"phone");

        //输出结果
        System.out.println(tm2);

    }
}
        运行结果:

        小练习2:

                需求2:

                键:学生对象

                值:籍贯

                要求:按照学生年龄的升序排列,

                           年龄一样按照姓名的字母排列,

                           同姓名年龄视为同一个人

        代码演示:
                Student类:
public class Student implements Comparable<Student>{
    private String name;
    private int age;


    public Student() {
    }

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

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

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

    /**
     * 获取
     * @return age
     */
    public int getAge() {
        return age;
    }

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

    public String toString() {
        return "Student{name = " + name + ", age = " + age + "}";
    }

    @Override
    public int compareTo(Student o) {
        int i = this.getAge() - o.getAge();
        i = i == 0 ? this.getName().compareTo(o.getName()) : i;
        return i;
    }
}
                测试类TreeMapDemo2:
public class TreeMapDemo2 {
    public static void main(String[] args) {
        /*
        需求2:
        键:学生对象
        值:籍贯
        要求:按照学生年龄的升序排列,
              年龄一样按照姓名的字母排列,
              同姓名年龄视为同一个人
         */

        //创建学生对象
        Student s1 = new Student("zhao",21);
        Student s2 = new Student("han",21);
        Student s3 = new Student("ma",22);
        Student s4 = new Student("zhao",21);

        //创建TreeMap对象
        TreeMap<Student,String> tm = new TreeMap<>();

        //添加学习对象
        tm.put(s1,"bd");
        tm.put(s2,"sjz");
        tm.put(s3,"hd");
        tm.put(s4,"tj");

        //输出结果
        System.out.println(tm);
    }
}
        运行结果:

        小练习3:

                需求: 字符串“aababcabcdabcde”

                统计字符串中每一个字符出现的次数,

                并按照以下格式输出

                输出结果:a(5)b(4)c(3)d(2)e(1)

        代码演示:
public class TreeMapDemo3 {
    public static void main(String[] args) {
        /*
        需求:
        字符串“aababcabcdabcde”
        统计字符串中每一个字符出现的次数,并按照以下格式输出
        输出结果:a(5)b(4)c(3)d(2)e(1)
         */

        String str = "aababcabcdabcde";
        //需要同时存储字符和其出现的次数,采用双列集合
        //需要按照次数从大到小排序,采用TreeMap
        //创建TreeMap
        TreeMap<Character,Integer> tm = new TreeMap<>();

        //添加元素
        for (int i = 0; i < str.length(); i++) {
            if(tm.containsKey(str.charAt(i))) {
                //若已存在 值+1
                tm.put(str.charAt(i),tm.get(str.charAt(i)) + 1);
            } else {
                //若不存在,put 值为1
                tm.put(str.charAt(i),1);
            }
        }

        //遍历
        StringBuilder sb = new StringBuilder();
        tm.forEach((key,  value) -> sb.append(key).append("(").append(value).append(")"));
        System.out.println(sb);

    }
}
        运行结果:

双列集合思考:

可变参数

        可变参数本质上是一个数组

作用:

        在形参中接收多个数据

格式:

        数据类型...参数名称(例如:int...args)

代码演示:

public class Args {
    public static void main(String[] args) {
        int sum1 = getSum();
        int sum2 = getSum(1,2,3,4);
        int sum3 = getSum(1,2,3,4,5);
        System.out.println(sum1);
        System.out.println(sum2);
        System.out.println(sum3);
    }

    public static int getSum(int...args) {
        int sum = 0;
        for (int i = 0; i < args.length; i++) {
            sum = sum + args[i];
        }
        return sum;
    }
}

运行结果:

注意:

        1.在方法的形参中最多只能写一个可变参数

        2.在方法中,如果除了可变参数以外,还有其他参数,那么可变参数要写最后

综合练习:

练习一:

        自动点名器1:

        班级里有N个学生,学生属性:姓名,年龄,性别。

        实现随机点名器

代码演示:
public class Test1 {
    public static void main(String[] args) {
        /*
        班级里有N个学生,学生属性:姓名,年龄,性别。
        实现随机点名器
         */


        //第一种方法
        System.out.println("第一种方法:");
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list,new Student("han",21,"m"),
                new Student("ma",22,"m"),new Student("zhao",21,"m"),
                new Student("wang",22,"m"),new Student("hu",22,"m"),
                new Student("wu",21,"f"));
        Random r = new Random();
        int index = r.nextInt(list.size());
        System.out.println(list.get(index).getName());

        //第二种方法
        System.out.println("第二种方法:");
        Collections.shuffle(list);
        System.out.println(list.get(0).getName());
    }
}
运行结果:

练习二:

        自动点名器2:

        班级里有N个学生,学生属性:姓名,年龄,性别。

        实现随机点名器

        要求:百分之70概率随机到男生,百分之30概率随机到女生

代码演示:
public class Test2 {
    public static void main(String[] args) {
        /*
        自动点名器2:
        班级里有N个学生,学生属性:姓名,年龄,性别。
        实现随机点名器
        要求:百分之70概率随机到男生,百分之30概率随机到女生
         */

        //存储学生信息
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list,new Student("han",21,"m"),
                new Student("ma",22,"m"),new Student("zhao",21,"m"),
                new Student("wang",22,"m"),new Student("hu",22,"m"),
                new Student("wu",21,"f"), new Student("xiaomei",21,"f"),
                new Student("xiaofang",22,"f"));

        //创建集合来控制概率
        ArrayList<Integer> probabilityList = new ArrayList<>();
        Collections.addAll(probabilityList,1,1,1,1,1,1,1);
        Collections.addAll(probabilityList,0,0,0);
        Collections.shuffle(probabilityList);
        int number = probabilityList.get(0);
        if(number == 1) {
            System.out.print("男生:");
        } else {
            System.out.print("女生:");
        }

        //创建集合分别存储男生和女生
        ArrayList<Student> boyList = new ArrayList<>();
        ArrayList<Student> girlList = new ArrayList<>();
        if(number == 1) {
            //如果要选男生
            //将所有男生存入一个集合中
            for (Student student : list) {
                if(student.getGender() == "m") {
                    boyList.add(student);
                }
            }
            //并随机抽取
            Collections.shuffle(boyList);
            System.out.println(boyList.get(0).getName());
        } else {
            //如果要选女生
            //将所有女生存入一个集合中
            for (Student student : list) {
                if(student.getGender() == "f") {
                    girlList.add(student);
                }
            }
            //并随机抽取
            Collections.shuffle(girlList);
            System.out.println(girlList.get(0).getName());
        }



    }
}
运行结果:

练习三:

        自动点名器3:

        班级里有N个学生,学生属性:姓名,年龄,性别。

        实现随机点名器

        要求:被点到的学生不会再被点到

        如果班级中所有的学生都点完了,需要重新开启第二轮点名

代码演示:
public class Test3 {
    public static void main(String[] args) {
        /*
        自动点名器3:
        班级里有N个学生,学生属性:姓名,年龄,性别。
        实现随机点名器
        要求:被点到的学生不会再被点到
        如果班级中所有的学生都点完了,需要重新开启第二轮点名
         */

        //存储学生信息
        ArrayList<Student> list = new ArrayList<>();
        Collections.addAll(list,new Student("han",21,"m"),
                new Student("ma",22,"m"),new Student("zhao",21,"m"),
                new Student("wang",22,"m"),new Student("hu",22,"m"),
                new Student("wu",21,"f"), new Student("xiaomei",21,"f"),
                new Student("xiaofang",22,"f"));

        //创建新集合存储数据
        ArrayList<Student> copyList = new ArrayList<>();
        copyList.addAll(list);

        Scanner sc = new Scanner(System.in);
        while (true) {
            //点名控制界面
            System.out.println("点名请输入:1,退出请输入:2");
            int num = sc.nextInt();
            //判断
            if(num == 1) {
                if(copyList.size() != 0){
                    //新集合长度不为0时
                } else {
                    //新数组长度为0
                    copyList.addAll(list);
                }
                //打乱集合
                Collections.shuffle(copyList);
                //获取随机姓名
                System.out.println(copyList.get(0).getName());
                //删除被点到的学生对象
                copyList.remove(0);
            }
            else {
                //退出
                break;
            }
        }

    }
}
运行结果:

不可变集合

        不可改变的集合

      特点:

                不可添加、不可删除、不可修改、只能查询

      代码演示:

                (利用List、Set、Map接口创建不可变集合,并展示查询及遍历方法)

        List:(Set与List方法一致)

public class ImmutableDemo1 {
    public static void main(String[] args) {
        //创建集合
        List<String> list = List.of("han", "ma", "zhao", "hu", "wang", "wu");

        //遍历集合
        //逐个遍历
        System.out.println(list.get(0));
        System.out.println(list.get(1));
        System.out.println(list.get(2));
        System.out.println(list.get(3));
        System.out.println(list.get(4));
        System.out.println(list.get(5));
        //普通for遍历
        System.out.println("----------------------");
        for (int i = 0; i < list.size(); i++) {
            System.out.println(list.get(i));
        }
        //迭代器
        System.out.println("----------------------");
        Iterator<String> it = list.listIterator();
        while(it.hasNext()) {
            String str = it.next();
            System.out.println(str);
        }
        //增强for
        System.out.println("----------------------");
        for (String str : list) {
            System.out.println(str);
        }
        //lambda表达式
        System.out.println("----------------------");
        list.forEach(str -> System.out.println(str));
    }
}
        运行结果:

                 

        Map:

public class ImmutableDemo2 {
    public static void main(String[] args) {
        //Map系列不可变集合

        //JDK10以上
        //先创建一个可变集合
        HashMap<String,String> hm = new HashMap<>();
        hm.put("111","aaa");
        hm.put("222","bbb");
        hm.put("333","ccc");
        hm.put("444","ddd");
        hm.put("555","eee");

        //转为不可变集合
        Map<String, String> map1 = Map.copyOf(hm);
        System.out.println(map1);

        //JDK10以下
        Map<Object, Object> map2 = Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]));
        System.out.println(map2);
    }
}
        运行结果:

注意:

        创建Set和Map的不可变集合时,要注意不能包含重复元素,并且按照各自集合的要求存储元素。

        Map不可变集合中最多只能存储20个元素,即10个键值对,超过十个用ofEntries方法

  • 38
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值