当前位置: 首页 > news >正文

java学习笔记10——集合框架

枚举类的使用

Collection接口继承树

Map接口继承树

Collection 接口方法

 总结:

集合框架概述
1.内存层面需要针对于多个数据进行存储。此时,可以考虑的容器有:数组、集合类

2.数组存储多个数据方面的特点:
    > 数组一旦初始化,其长度就是确定的。
    > 数组中的多个元素是依次紧密排列的,有序的,可重复的
    > (优点)数组一旦初始化完成,其元素的类型就是确定的。不是此类型的元素,就不能添加到此数组中。
        int[] arr = new int[10];
        arr[0]= 1;
        arr[1]="AA";//编译报错
        Object[] arr1 = new Object[10];
        arr1[0] = new String();
        arr1[1] = new Date();
    > (优点)元素的类型既可以是基本数据类型,也可以是引用数据类型

    数组存储多个数据方面的弊端:
    > 数组一旦初始化,其长度就不可变了。
    > 数组中存储数据特点的单一性。对于无序的、不可重复的场景的多个数据就无能为力了。
    > 数组中可用的方法、属性都极少。具体的需求,都需要自己来组织相关的代码逻辑。
    > 针对于数组中元素的删除、插入操作,性能较差。

3.Java集合框架体系(java.util包下)
java.util.Collection:存储一个一个的数据
    |-----子接口:List:存储有序的、可重复的数据 ("动态"数组)
        |---- ArrayList(主要实现类)、LinkedList、Vector
    |-----子接口:Set:存储无序的、不可重复的数据 (高中学习的集合)
        |----HashSet(主要实现类)、LinkedHashSet、TreeSet
java.util.Map:存储一对一对的数据(key-value键值对,(x1,y1)、(x2,y2) --> y=f(x),类似于高中的函数)
    |---- HashMap(主要实现类)、LinkedHashMap、TreeMap、Hashtable、Properties

4.学习的程度把握
层次1:针对于具体特点的多个数据,知道选择相应的适合的接口的主要实现类,会实例化,会调用常用的方法。
层次2:区分接口中不同的实现类的区别。
**********************************
层次3:① 针对于常用的实现类,需要熟悉底层的源码 ② 熟悉常见的数据结构(第14章讲)

总结2:Collection中的常用方法

1.常用方法:(Collection中定义了15个抽象方法。这些方法需要大家熟悉!)
add(Object obj)
addAll(Collection coll)
clear()
isEmpty()
size()
contains(Object obj)
containsAll(Collection coll)
retainAll(Collection coll)
remove(Object obj)
removeAll(Collection coll)
hashCode()
equals()
toArray()
*************
iterator()

2.集合与数组的相互转换:
集合 ---> 数组:toArray()
数组 ---> 集合:调用Arrays的静态方法asList(Object... objs)
3.向Collection中添加元素的要求:
    要求元素所属的类一定要重写equals()!
原因:
因为Collection中的相关方法(比如:contains() / remove())在使用时,要调用元素所在类的equals()

相关代码:测试Collection中一些相关方法的使用

public class CollectionTest {
    /**
     * (1)add(Object obj):添加元素对象到当前集合中
     * (2)addAll(Collection other):添加other集合中的所有元素对象到当前集合中,即this = this U other
     */
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add(123); //自动装箱
        coll.add("尚硅谷");
        coll.add(new Object());
        coll.add(new Person("Tom",12));
        System.out.println(coll);
        //addAll(Collection other)
        Collection coll1 = new ArrayList();
        coll1.add("BB");
        coll1.add(456);
        System.out.println(coll.size());
        coll.addAll(coll1); //[AA, 123, 尚硅谷, java.lang.Object@36d64342, Person{name='Tom', age=12}, BB, 456]
//        coll.add(coll1); // [AA, 123, 尚硅谷, java.lang.Object@511baa65, Person{name='Tom', age=12}, [BB, 456]]
        System.out.println(coll);

        //size():
        System.out.println(coll.size());
    }

    /**
     * (3)int size():获取当前集合中实际存储的元素个数
     * (4)boolean isEmpty():判断当前集合是否为空集合
     * (5)boolean contains(0bject obj):判断当前集合中是否存在一个与obj对象equals返回true的元素
     * (6)boolean containsAll(Collection coll):判断col集合中的元素是否在当前集合中都存在。即col集合是否是当前集合的“子集”
     * (7)boolean equals(0bject obj):判断当前集合与obj是否相等
     */
    @Test
    public void test2(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add("尚硅谷");
        coll.add(new Object());

        //isEmpty()
        System.out.println(coll.isEmpty()); //false
        //contains(0bject obj)
        System.out.println(coll.contains("AA")); //true
        System.out.println(coll.contains(128)); //true
        System.out.println(coll.contains(new String("尚硅谷")));//true
        System.out.println(coll.contains(new Person("Tom",12))); //false  ---> 重写后为true

        //containsAll(Collection coll)
        Collection coll1 = new ArrayList();

        //add()
        coll1.add("AA");
        coll1.add(128); //true
//        coll1.add("BB"); //false
        System.out.println(coll.containsAll(coll1));

    }

    /**
     * (8)void clear():清空集合元素
     * (9)boolean remove(0bject obj):从当前集合中删除第一个找到的与obj对象equals返回true的元素。
     * (10)boolean removeAll(Collection coll):从当前集合中删除所有与col集合中相同的元素。即this = this-this ∩ coll
     * (11)boolean retainAll(collection coll):从当前集合中删除两个集合中不同的元素,使得当前集合仅保留与coll集合中的元素相同的元素,
     * 即当前集合中仅保留两个集合的交集,即this=this ∩ coll;
     * 注意几种删除方法的区别
     */
    @Test
    public void test3(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add("AA");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add(new String("尚硅谷"));
//        coll.clear();
//        System.out.println(coll);//[]
//        System.out.println(coll.size()); //0

        //remove(Object obj)
//        coll.remove(p1);
//        coll.remove(new Person("Tom",12)); //能删除,已经被重写过
        coll.remove("AA"); //只删除一个
        System.out.println(coll);
    }
    /**
     * (12)Object[] toArray():返回包含当前集合中所有元素的数组
     * (13)hashCode():获取集合对象的哈希值
     * (14)iterator():返回迭代器对象,用于集合遍历
     */
    @Test
    public void test4(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add("AA");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add(new String("尚硅谷"));

        //集合 ---> 数组
        Object[] arr = coll.toArray();
        System.out.println(Arrays.toString(arr));
    }
    @Test
    public void test5(){
        String[] arr = new String[]{"AA","BB","CC"};
        Collection list = Arrays.asList(arr);
        System.out.println(list); //[AA, BB, CC]
        List list1 = Arrays.asList("AA","BB","CC");
        System.out.println(list1.size()); //[AA, BB, CC] 3
    }
    @Test
    public void test6(){
        Integer[] arr = new Integer[]{1,2,3};
        List list = Arrays.asList(arr);
        System.out.println(list.size()); //3
        System.out.println(list); //[1, 2, 3]

        int[] arr1 = new int[]{1,2,3};
        List list1 = Arrays.asList(arr1);
        System.out.println(list1.size()); //1
        System.out.println(list1); //[[I@7f63425a]
    }
}

Person 
public class Person {
    String name;
    int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        System.out.println("Person equals()...");
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
}

Iterator迭代器接口

 总结:

1.迭代器(Iterator)的作用?
用来遍历集合元素的。
2.如何获取选代器(Iterator)对象?
Iterator iterator = coll.iterator();
3.如何实现遍历(代码实现)
while(iterator.hasNext()){
    System.out.println(iterator.next());//next():①指针下移 ② 将下移以后集合位置上的元素返回
}
4.增强for循环(foreach循环)的使用(jdk5.0新特性)
4.1 作用
用来遍历数组、集合。
4.2 格式:
for(要遍历的集合或数组元素的类型 临时变量 : 要遍历的集合或数组变量){
    操作临时变量的输出
}
4.3 说明:
> 针对于集合来讲,增强for循环的底层仍然使用的是选代器。
> 增强for循环的执行过程中,是将集合或数组中的元素依次赋值给临时变量,注意,循环体中对临时变量的修改,可能
    不会导致原有集合或数组中元素的修改。

相关代码:

public class IteratorTest {
    @Test
    public void test1(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add("AA");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add(new String("尚硅谷"));

        //获取迭代器对象
        Iterator iterator = coll.iterator();
//        System.out.println(iterator.getClass());
        //方式1:
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next());
//        System.out.println(iterator.next()); //如果超出了集合中元素的个数,会报NoSuchElementException异常信息

        //方式2:
//        for (int i = 0; i < coll.size(); i++) {
//            System.out.println(iterator.next());
//        }
        //方式3:推荐
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        };
    }
    @Test
    public void test2(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add("BB");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add(new String("尚硅谷"));
        //方式1:错误的遍历
        /**
         * BB
         * 128
         * 报错NoSuchElementException
         */
//        Iterator iterator = coll.iterator();
//        while(iterator.next() != null){
//            System.out.println(iterator.next());
//        }
        /**
         * AA
         * ...
         * ...
         */
        //方式2:错误的遍历
        //每次调用coll.iterator(),都会返回一个新的迭代器对象
        while(coll.iterator().hasNext()){
            System.out.println(coll.iterator().next());
        }
    }
}

2.ForTest 
public class ForTest {
    @Test
    public void test(){
        Collection coll = new ArrayList();
        //add()
        coll.add("AA");
        coll.add("BB");
        Person p1 = new Person("Tom",12);
        coll.add(p1);
        coll.add(128); //自动装箱
        coll.add(new String("尚硅谷"));

        for(Object obj : coll){
            System.out.println(obj);
        }
    }
    @Test
    public void test2(){
        int[] arr = new int[]{1,2,3,4,5};
        for(int i : arr){
            System.out.println(i);
        }
        String[] str = new String[]{"GG","JJ","DD","MM","SS"};
        for(String s : str){
            System.out.println(s);
        }
    }
}

 面试题:

public class InterviewTest {
    @Test
    public void testFor(){
        String[] arr1 = new String[]{"AA","CC","DD"};
        //赋值操作1
//        for(int i = 0; i < arr1.length; i++){
//            arr1[i] = "MM";
//        }
        //赋值操作2,两个都是把地址传递过来,但是这个地方是把地址赋给新的变量,然后更改这个新的变量
        for(String s : arr1){
            s = "MM";
        }
        //遍历
        for(String s : arr1){
            System.out.println(s);
        }
    }
}

Collection子接口之一:List接口

List实现类之一:ArrayList

List实现类之二:LinkedList

List 实现类之三:Vector

总结: 

1.List接口中存储数据的特点:
用于存储有序的、可以重复的数据。---> 使用List替代数组,"动态"数组
2.List中的常用方法
第1波:Collection中声明的15个方法,

第2波: 因为List是有序的,进而就有索引,进而就会增加一些针对索引操作的方法。
- 插入元素
    - void add(int index,Object ele):在index位置插入ele元素
    - boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
- 获取元素
    - Object get(int index):获取指定index位置的元素
    - List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合
- 获取元素索引
    - int index0f(Object obj):返回obj在集合中首次出现的位置
    - int lastIndex0f(Object obj):返回obj在当前集合中末次出现的位置
- 删除和替换元素
    - Object remove(int index):移除指定index位置的元素,并返回此元素
    - Object set(int index, Object ele):设置指定index位置的元素为ele
小结:
    增
        add(Object obj)
        addAll(Collection coll)
    删
        remove(Object obj)
        remove(int index)
    改
        set(int index, Object ele)
    查
        get(int index)
    插
        add(int index,Object ele)
        addAll(int index,Collection eles)
    长度
        size()
    遍历
        iterator():使用迭代器进行遍历
        增强for循环
        一般的for循环

3.List及其实现类特点
java.util.Collection:存储一个一个的数据
    |-----子接口:List:存储有序的、可重复的数据 ("动态"数组)
        |---- ArrayList: List的主要实现类,线程不安全的、效率高,底层使用Object[]数组存储
                          在添加数据、查找数据时(O(1)),效率较高;在插入、删除数据时,效率较低 (O(n))
        |---- LinkedList:底层使用双向链表的方式进行存储,在对集合中的数据进行频繁的删除、插入操作时,建议使用此类
                            在插入、删除数据时(O(1)),效率较高;在添加数据、查找数据时(O(n)),效率较低
        |---- Vector: List的古老实现类,线程安全的、效率低,底层使用Object[]数组存储

在插入和删除数据的时候,对于ArrayList类而言,会影响到当前索引的后面元素,而对于LinkedList这种双向链表来说却不会影响到很多元素,只是改变周围元素的指向,双向链表是通过指针连接的
[面试题] ArrayList、Vector的区别? ArrayList、LinkedList的区别?

代码:

public class ListTest {
    /**
     *     增
     *         add(Object obj)
     *         addAll(Collection coll)
     *     删
     *         remove(Object obj)
     *         remove(int index)
     *     改
     *         set(int index, Object ele)
     *     查
     *         get(int index)
     *     插
     *         add(int index,Object ele)
     *         addAll(int index,Collection eles)
     *     长度
     *         size()
     *     遍历
     *         iterator():使用迭代器进行遍历
     *         增强for循环
     *         一般的for循环
     */
    @Test
    public void test(){
        List list = new ArrayList();
        //add(Object obj)
        list.add("AA");
        list.add("BB");
        list.add(123); //自动装箱
        list.add(new Person("Tom",12));
        System.out.println(list.toString());
        // add(int index,Object ele)
        list.add(2,"CC");
        System.out.println(list);
//        addAll(int index,Collection eles)
        List list1 = Arrays.asList(1,2,3);
        list.addAll(1,list1); //[AA, 1, 2, 3, BB, CC, 123, Person{name='Tom', age=12}]
        //将list1整体作为一个元素,插入到索引1的位置
//        list.add(1,list1); //[AA, [1, 2, 3], BB, CC, 123, Person{name='Tom', age=12}]
        System.out.println(list);
    }
    @Test
    public void test2(){
        List list = new ArrayList();
        list.add("AA");
        list.add("BB");
        list.add(123); //自动装箱
        list.add(2); //自动装箱
        list.add(new Person("Tom",12));
        //删除索引2的元素
//        list.remove(2);
//        System.out.println(list);
//        System.out.println(list.get(2));
        //删除数据2
        list.remove(Integer.valueOf(2));
        System.out.println(list);
    }
    @Test
    public void test3(){
        List list = new ArrayList();
        list.add("AA");
        list.add("BB");
        list.add(123); //自动装箱
        list.add(2); //自动装箱
        list.add(new Person("Tom",12));
        //遍历方式1:使用迭代器
//        Iterator iterator = list.iterator();
//        while(iterator.hasNext()){
//            System.out.println(iterator.next());
//        }
        //遍历方式2:增强for循环
//        for (Object obj : list){
//            System.out.println(obj);
//        }
        //遍历方式3:一般的for循环
        for(int i = 0;i<list.size();i++){
            System.out.println(list.get(i));
        }
    }
}
Person 
public class Person {
    String name;
    int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        System.out.println("Person equals()...");
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
}

练习题:

案例:键盘录入学生信息,保存到集合List中。
(1)定义学生类:属性为姓名、年龄,提供必要的getter、setter方法,构造器,toString(),equals()方法。
(2)使用ArrayList集合,保存录入的多个学生对象。
(3)循环录入的方式,1:继续录入,0:结束录入。
(4)录入结束后,用foreach遍历集合:
StudentTest 
public class StudentTest {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        List list = new ArrayList();
        System.out.println("请录入学生信息:");
        //通过循环添加多个学生信息
        while(true) {
            System.out.println("1.继续录入,0.结束录入");
            int selection = scan.nextInt();
            if(selection == 0){
                break;
            }
            System.out.print("请输入学生的姓名:");
            String name = scan.next();
            System.out.print("请输入学生的年龄:");
            int age = scan.nextInt();
            Student s = new Student(name, age);
            list.add(s);
        }
        //遍历集合中的学生信息
        System.out.println("遍历学生信息");
        for(Object s : list){
            Student stu = (Student)s;
            System.out.println(stu.toString());
        }
        scan.close();
    }
}
Student 
public class Student {
    private String name;
    private int age;

    public Student() {
    }

    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 +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        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);
//    }
}

练习2

案例:
定义方法public static int listTest(Collection list,String s)统计集合中指定元素出现的次数
(1)创建集合,集合存放随机生成的30个小写字母
(2)用listTest统计,a、b、c、x元素的出现次数
(3)效果如下
public class ListTest {
    public static void main(String[] args) {
        //需求1:随机生成30个字符,存放在ArrayList中
        ArrayList list = new ArrayList();
        for(int i = 0; i < 30; i++){
            //'a' - 'z'  [97,122]  (char)强制类型转换
            list.add((char)(Math.random() * (122-97 +1) + 97) + "");
        }
        System.out.println(list);
        //
        int aCount = listTest(list, "a");
        int bCount = listTest(list, "b");
        int cCount = listTest(list, "c");
        int xCount = listTest(list, "x");
        System.out.println(aCount);
        System.out.println(bCount);
        System.out.println(cCount);
        System.out.println(xCount);
    }
    //需求2:遍历ArrayList,查找指定的元素出现的次数
    public static int listTest(Collection list, String s){
        int count = 0;
        for(Object obj : list){
            if(s.equals(obj)){
                count++;
            }
        }
        return count;
    }
}

练习3:

案例:KTV点歌系统

【说明】
使用ArrayList集合,实现编写一个模拟KTV点歌系统的程序。在程序中,
指令1代表添加歌曲
指令2代表将所选歌曲置顶
指令3代表将所选歌曲提前一位,
指令4代表退出该系统。

要求根据用户输入的指令完成相关的操作:

【提示】
(1)显式界面如下:
System.out.println("----------欢迎来到点歌系统------------");
System.out.println("1.添加歌曲至列表");
System.out.println("2.将歌曲置顶");
System.out.println("3.将歌曲前移一位");
System.out.println("4.退出");

(2)程序中需要创建一个集合作为歌曲列表,并向其添加一部分歌曲
public class KTVByArrayList {
    private static ArrayList musicList = new ArrayList();//创建歌单列表
    private static Scanner sc = new Scanner(System.in);

    public static void main(String[] args) {
        addMusicList(); //添加一部分歌曲至歌曲列表
        boolean flag = true;
        while (flag) {
            System.out.println("当前歌曲列表:" + musicList);
            System.out.println("------------欢迎来到点歌系统-----------");
            System.out.println("1.添加歌曲至列表");
            System.out.println("2.将歌曲置顶");
            System.out.println("3.将歌曲前移一位");
            System.out.println("4.退出");
            System.out.print("请输入操作序号:");
            int key = sc.nextInt(); //接收键盘输入的功能选项序号
            //执行序号对应的功能
            switch (key) {
                case 1://添加歌曲至列表
                    addMusic();
                    break;
                case 2://将歌曲置顶
                    setTop();
                    break;
                case 3://将歌曲前移一位
                    setBefore();
                    break;
                case 4://退出
                    System.out.println("----------退出----------");
                    System.out.println("您已退出系统");
                    flag = false;
                    break;
                default:
                    System.out.println("----------------------");
            }
        }
    }
    //初始时添加歌曲名称
    public static void addMusicList() {
        musicList.add("本草钢目");
        musicList.add("你是我的眼");
        musicList.add("老男孩");
        musicList.add("白月光与朱砂痣");
        musicList.add("不谓侠");
        musicList.add("爱你");
    }
    //执行添加歌曲
    private static void addMusic() {
        System.out.print("请输入要添加的歌曲名称:");
        String musicName = sc.next();
        musicList.add(musicName); //添加歌曲到列表的最后
        System.out.println("已添加歌曲" + musicName);
    }
    //执行将歌曲置顶
    private static void setTop() {
        System.out.print("请输入要置顶的歌曲名称:");
        String musicName = sc.next(); //获取键盘输入内容
        int musicIndex = musicList.indexOf(musicName); //查找指定歌曲位置
        if(musicIndex < 0){ //判断歌曲是否存在
            System.out.println("当前列表中没有输入的歌曲!");
        }else if(musicIndex == 0){
            System.out.println("当前歌曲默认已置顶!");
        }else{
            musicList.remove(musicName); //移除指定的歌曲
            musicList.add(0,musicName); //将指定的歌曲放在第一位
            System.out.println("已将歌曲《"+musicName+"》置顶");
        }
    }
    //执行将歌曲置顶前一位
    private static void setBefore() {
        System.out.print("请输入要置前的歌曲名称:");
        String musicName = sc.next(); //获取键盘输入内容
        int musicIndex = musicList.indexOf(musicName); //查找指定歌曲位置
        if(musicIndex < 0){ //判断输入歌曲是否存在
            System.out.println("当前列表中没有输入的歌曲!");
        }else if(musicIndex == 0){ //判断歌曲是否已在第一位
            System.out.println("当前歌曲已在最顶部!");
        }else {
            musicList.remove(musicName); //移除指定的歌曲
            musicList.add(musicIndex - 1,musicName); //将指定的歌曲放在前一位
            System.out.println("已将歌曲《" + musicName + "》置前一位");
        }
    }
}

面试题:

public class InterviewTest {
    @Test
    public void testListRemove(){
        List list = new ArrayList();
        list.add(1); //自动装箱
//        list.add(2);
        list.add(128);
        list.add(3);
        updateList(list);
        System.out.println(list);//
    }
    private static void updateList(List list){
//        list.remove(2); //删除的是索引2
        list.remove(Integer.valueOf(128)); //[1, 3]
    }
}

Collection子接口之二:Set接口

Set实现类之一:HashSet

Set实现类之二:LinkedHashSet

Set实现类之三:TreeSet

排 序自然排序

排 序定制排序

总结:

1.Set及其实现类特点
java.util.Collection:存储一个一个的数据
    |-----子接口:Set:存储无序的、不可重复的数据(高中学习的集合)
        |---- Hashset(主要实现类):底层使用的是HashMap,即使用数组+单向链表+红黑树结构进行存储。
            |---- LinkedHashSet:是HashSet的子类;在现有的数组+单向链表+红黑树结构的基础上,又添加了
                                一组双向链表,用于记录添加元素的先后顺序。即:我们可以按照添加元素的顺序,实现遍历。
                                便于我们频繁的查询操作
        |---- TreeSet:底层使用红黑树存储。可以按照添加的元素的指定的属性的大小顺序进行遍历。

2.开发中的使用频率及场景:
> 较List、Map来说,Set使用的频率比较少。
> 用来过滤重复数据

3.Set中常用方法:即为Collection中声明的15个抽象方法,没有新增的方法

4.Set中无序性、不可重复性的理解(以HashSet及其子类为例说明)
> 无序性: != 随机性。
    添加元素的顺序和遍历元素的顺序不一致,是不是就是无序性呢? No!
    到底什么是无序性?与添加的元素的位置有关,不像ArrayList一样是依次紧密排列的。
    这里是根据添加的元素的哈希值,计算的其在数组中的存储位置。此位置不是依次排列的,表现为无序性。
> 不可重复性: 添加到Set中的元素是不能相同的。
            比较的标准,需要判断hashcode()得到的哈希值以及equals()得到的boolean型的结果。
            哈希值相同且equals()返回true,则认为元素是相同的。
5.添加到HashSet/LinkedHashSet中元素的要求:
    要求元素所在的类要重写两个方法:equals()和hashcode()
    同时,要求equals()和 hashcode()要保持一致性!我们只需要在IDEA中自动生成两个方法的重写即可,即能保证两个方法的一致性

6.TreeSet的使用
6.1 底层的数据结构: 红黑树

6.2 添加数据后的特点: 可以按照添加的元素的指定的属性的大小顺序进行遍历。
6.3向Treeset中添加的元素的要求:
> 要求添加到TreeSet中的元素必须是同一个类型的对象,否则会报ClassCastException.
> 添加的元素需要考虑排序:① 自然排序 ② 定制排序
6.4判断数据是否相同的标准
> 不再是考虑hashcode()和equals()方法了,也就意味着添加到TreeSet中的元素所在的类不需要重写hashCode()和equals()方法了
> 比较元素大小的或比较元素是否相等的标准就是考虑自然排序或定制排序中,compareTo()或compare()的返回值。
  如果compareTo()或compare()的返回值为0,则认为两个对象是相等的。由于TreeSet中不能存放相同的元素,则
  后一个相等的元素就不能添加到TreeSet中。

代码:
 

SetTest 
public class SetTest {
    @Test
    public void test1(){
        /**
         * AA
         * BB
         * Person{name='Tom', age=12}
         * 123
         * false
         */
        Set set = new HashSet();
        set.add("AA");
        set.add(123);
        set.add("BB");
        set.add(new Person("Tom",12));
        set.add(new Person("Tom",12));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println(set.contains(new Person("Tom",12))); //false
    }
    @Test
    public void test2(){
        /**
         * AA
         * 123
         * BB
         * Person{name='Tom', age=12}
         * false
         */
        Set set = new LinkedHashSet();
        set.add("AA");
        set.add(123);
        set.add("BB");
        set.add(new Person("Tom",12));

        Iterator iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }
        System.out.println(set.contains(new Person("Tom",12))); //false
    }
}
Person 
public class Person {
    String name;
    int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        System.out.println("Person equals()...");
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }

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

代码2:
 

TreeSetTest 
public class TreeSetTest {
    /**
     * 自然排序
     */
    @Test
    public void test1(){
        /**
         * AA
         * CC
         * DD
         * GG
         * MM
         * SS
         */
        TreeSet set = new TreeSet();
        set.add("CC");
        set.add("AA");
        set.add("DD");
        set.add("MM");
        set.add("GG");
        set.add("SS");
//        set.add(123); //会报ClassCastException异常
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }

    /**
     * 自然排序
     */
    @Test
    public void test2(){
        TreeSet set = new TreeSet();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",43);
        User u3 = new User("Rose",13);
        User u4 = new User("Jack",23);
        User u5 = new User("Tony",33);
        set.add(u1);
        set.add(u2);
        set.add(u3);
        set.add(u4);
        set.add(u5);
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
    /**
     * 定制排序
     */
    @Test
    public void test3(){
        Comparator comparator = new Comparator(){
            /**
             * 按照姓名从小到大排序,如果姓名相同,继续比较age,按照从大到小的排列
             */
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    int value = u1.getName().compareTo(u2.getName());
                    if(value != 0){
                        return value;
                    }
                    return -(u1.getAge() - u2.getAge());
                }
                throw new RuntimeException("类型不匹配");
            }
        };
        TreeSet set = new TreeSet(comparator);
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",43);
        User u3 = new User("Rose",13);
        User u4 = new User("Jack",23);
        User u5 = new User("Tony",33);
        set.add(u1);
        set.add(u2);
        set.add(u3);
        set.add(u4);
        set.add(u5);
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}
User 
public class User implements Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(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 "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

//    @Override
//    public boolean equals(Object o) {
//        System.out.println("User equals()...");
//        if (o == null || getClass() != o.getClass()) return false;
//        User user = (User) o;
//        return age == user.age && Objects.equals(name, user.name);
//    }
//
//    @Override
//    public int hashCode() {
//        return Objects.hash(name, age);
//    }

    /**
     * 按照年龄的从小到大排序
     * @param o the object to be compared.
     * @return
     */
//    @Override
//    public int compareTo(@NotNull Object o) {
//        if(this == o) {
//            return 0;
//        }
//        if(o instanceof User){
//            User u = (User)o;
//            return this.age - u.age;
//        }
//        throw new RuntimeException("类型不匹配");
//    }

    /**
     * 比如:先比较年龄从小到大排列,如果年龄相等,则继续比较姓名,从小到大
     */
    @Override
    public int compareTo(@NotNull Object o) {
        if(this == o) {
            return 0;
        }
        if(o instanceof User){
            User u = (User)o;
            int value = this.age - u.age;
            if(value != 0){
                return value;
            }
            return -this.name.compareTo(u.name);
        }
        throw new RuntimeException("类型不匹配");
    }
}

练习1

 案例: 定义方法如下:public static List duplicateList(List list) 要求:① 参数List中只存放Integer的对象 ② 在List内去除重复数字值,尽量简单

代码:

public class Exer01 {
    public static void main(String[] args) {
        ArrayList list = new ArrayList();
        list.add(34);
        list.add(34);
        list.add(34);
        list.add(22);
        list.add(22);
        list.add(22);
        list.add(45);
        list.add(45);
        list.add(45);
        List newList = duplicateList(list);
        System.out.println(newList);
    }
    public static List duplicateList(List list) {
        //方式1:
//        HashSet set = new HashSet();
//        for(Object obj : list){
//            set.add(obj);
//        }
//        List list1 = new ArrayList();
//        for(Object obj : set){
//            list1.add(obj);
//        }
//        return list1;

        //方式2
        HashSet set = new HashSet(list);
        List list1 = new ArrayList(set);
        return list1;
    }
}

练习2

 案例: 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。并把最终的随机数输出到控制台

代码:

/**
 * ClassName: Exero2
 * Package: com.atguigu04.set.exer2
 * Description:
 * 编写一个程序,获取10个1至20的随机数,要求随机数不能重复。并把最终的随机数输出到控制台
 * @Author: lwfstart
 * @Create 2025-03-31 13:25
 * @Version: 1.0
 */
public class Exer02 {
    public static void main(String[] args) {
        Set set = new HashSet();
        while(set.size() < 10){
            int random = (int) (Math.random() * (20 - 1 + 1) + 1);
            set.add(random);
        }
        Iterator iterator = set.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
        }
    }
}

 面试题:

public class HashSetDemo {
    public static void main(String[] args) {
        HashSet set = new HashSet();
        Person p1 = new Person(1001,"AA");
        Person p2 = new Person(1002,"BB");
        set.add(p1);
        set.add(p2);
        System.out.println(set);

        p1.name = "CC";
        set.remove(p1);
        System.out.println(set);
//
        set.add(new Person(1001,"CC"));
        System.out.println(set); //hash值不一样,可以添加进去

        set.add(new Person(1001,"AA")); //跟最初p1的hash值一样
        System.out.println(set);
    }
}
Person 
public class Person {
    double id;
    String name;

    public Person() {
    }

    public Person(double id, String name) {
        this.id = id;
        this.name = name;
    }
    //其中Person类中重写了hashCode()和equals()方法

    @Override
    public boolean equals(Object o) {
        System.out.println("Person equals...");
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Double.compare(id, person.id) == 0 && Objects.equals(name, person.name);
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

Map接口

Map实现类之一:HashMap

HashMap的存储结构

HashMap源码中的重要常量

Map实现类之二:LinkedHashMap

Map实现类之三:TreeMap

Map实现类之四:Hashtable

Map实现类之五:Properties

总结:

 

1.Map及其实现类对比
java.util.Map:存储一对一对的数据(key-value键值对,(x1,y1)、(x2,y2) --> y=f(x),类似于高中的函数)
    |---- HashMap(主要实现类):线程不安全的,效率高;可以添加null的key和value值;底层使用数组+单向链表+红黑树结构存储(jdk8)
        |---- LinkedHashMap:是HashMap的子类;在HashMap使用的数据结构的基础上,增加了一对双向链表,用于记录添加的元素的先后顺序,
                            进而我们在遍历元素时,就可以按照添加的顺序显示。
                            开发中,对于频繁的遍历操作,建议使用此类。
    |---- TreeMap:底层使用红黑树存储;可以按照添加的key-value中的key元素的指定的属性的大小顺序进行遍历。需要考虑使用①自然排序 ②定制排序。
    |---- Hashtable:古老实现类,线程安全的,效率低;不可以添加null的key或value值;底层使用数组+单向链表结构存储(jdk8)
        |---- Properties:其key和value都是String类型。常用来处理属性文件。



[面试题]:区别HashMap和Hashtable、区别HashMap和LinkedHashMap、HashMap的底层实现(① new HashMap() ② put(key,value))

2. HashMap中元素的特点
> HashMap中的所有的key彼此之间是不可重复的、无序的。所有的key就构成一个Set集合。--->key所在的类要重写hashCode()和equals()
> HashMap中的所有的value彼此之间是可重复的、无序的。所有的value就构成一个Collection集合。--->value所在的类要重写equals()
> HashMap中的一个key-value,就构成了-个entry。
> HashMap中的所有的entry彼此之间是不可重复的、无序的。所有的entry就构成了一个Set集合。
3.Map中的常用方法
- 添加、修改操作:
    - Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
    - void putAll(Map m):将m中的所有key-value对存放到当前map中
- 删除操作:
    - Object remove(Object key):移除指定key的key-value对,并返回value
    - void clear():清空当前map中的所有数据
- 元素查询的操作:
    - Object get(Object key):获取指定key对应的value
    - boolean containsKey(Object key):是否包含指定的key
    - boolean containsValue(Object value):是否包含指定的value
    - int size():返回map中key-value对的个数
    - boolean isEmpty():判断当前map是否为空
    - boolean equals(Object obj):判断当前map和参数对象obj是否相等
- 元视图操作的方法:
    - Set keySet():返回所有key构成的Set集合
    - Collection valves():返回所有value构成的Collection集合
    - Set entrySet():返回所有key-value对构成的Set集合
小结:
    增
        put(Object key,Object value)
        putALL(Map m)
    删
        Object remove(Object key)
    改:
        put(object key,Object value)
        putALL(Map m)
    查:
        Object get(object key)
    长度:
        size()
    遍历:
        遍历key集:Set keySet()
        遍历value集:Collection values()
        遍历entry集:Set entrySet()
4.TreeMap的使用
> 底层使用红黑树存储;
> 可以按照添加的key-value中的key元素的指定的属性的大小顺序进行遍历
> 需要考虑使用①自然排序 ②定制排序。
> 要求:向TreeMap中添加的key必须是同一个类型的对象。
5.Hashtable与Properties的使用
Properties:是Hashtable的子类,其key和value都是String类型的,常用来处理属性文件。

相关代码:
 

MapTest
public class MapTest {
    /**
     * 测试Map中的实现类
     */
    @Test
    public void test1(){
        Map map = new HashMap();
        map.put(null,null);
        System.out.println(map);
    }
    @Test
    public void test2(){
        Map map = new Hashtable();
//        map.put(null,123); //NullPointerException报异常
//        map.put("AA",null); //NullPointerException报异常
        System.out.println(map);
    }
    @Test
    public void test3(){
        LinkedHashMap map = new LinkedHashMap();
        map.put("Tom",23);
        map.put("CC",new Date());
        map.put(34,"AA");
        System.out.println(map);
    }
    /**
     * 测试Map中的常用方法
     *     增
     *         put(Object key,Object value)
     *         putALL(Map m)
     *     删
     *         Object remove(Object key)
     *     改:
     *         put(object key,Object value)
     *         putALL(Map m)
     *     查:
     *         Object get(object key)
     *     长度:
     *         size()
     *     遍历:
     *         遍历key集:Set keySet()
     *         遍历value集:Collection values()
     *         遍历entry集:Set entrySet()
     */
    @Test
    public void test4(){
        HashMap map = new HashMap();
        //添加:put(Object key,Object value)
        map.put("AA",56);
        map.put(67,"Tom");
        map.put("BB",78);
        map.put(new Person("Jerry",12),56);
        /**
         * {AA=56, BB=78, 67=Tom, Person{name='Jerry', age=12}=56}
         * 4
         */
        System.out.println(map);
        System.out.println(map.size());
        //Object remove(Object key)
        Object value = map.remove("AA");
        System.out.println(value);
        System.out.println(map);
        //修改:put(object key,Object value)
        Object oldValue = map.put("BB", 99);
        System.out.println(oldValue); //78
        System.out.println(map);
        //Object get(object key)
        Object value1 = map.get(67);
        System.out.println(value1); //Tom
    }
    //map的遍历操作
    @Test
    public void test5(){
        HashMap map = new HashMap();
        map.put("AA",56);
        map.put(67,"Tom");
        map.put("BB",78);
        map.put(new Person("Jerry",12),56);
        // 遍历key集:Set keySet()
        Set keySet = map.keySet();
        //使用迭代器
        Iterator iterator = keySet.iterator();
        while(iterator.hasNext()){
            Object key = iterator.next();
            System.out.println(key);
        }
        //遍历value集:Collection values()
        //方式1:
//        Collection values = map.values();
//        //使用增强for
//        for(Object obj : values){
//            System.out.println(obj);
//        }
        //方式2:
        Set keySet1 = map.keySet();
        for(Object key : keySet1){
            Object value = map.get(key);
            System.out.println(value);
        }
    }

    @Test
    public void test6(){
        HashMap map = new HashMap();
        map.put("AA",56);
        map.put(67,"Tom");
        map.put("BB",78);
        map.put(new Person("Jerry",12),56);
        //方式1:遍历entry集:Set entrySet()
        Set entrySet = map.entrySet();
        Iterator iterator = entrySet.iterator();
        while(iterator.hasNext()){
            //方式1:
            System.out.println(iterator.next());
            //方式2:
//            Map.Entry entry = (Map.Entry) iterator.next();
//            System.out.println(entry.getKey() + " ---> " + entry.getValue());
        }
        //方式2:遍历entry集:keySet()、get(key)
        Set keySet = map.keySet();
        for(Object key : keySet){
            System.out.println(key + " ---> " +map.get(key));
        }
    }
}

person类

public class Person {
    String name;
    int age;

    public Person() {
    }

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

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        System.out.println("Person equals()...");
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age && Objects.equals(name, person.name);
    }
}

 TreeMapTest 

public class TreeMapTest {
    /**
     * 自然排序
     */
    @Test
    public void test1(){
        TreeMap map = new TreeMap();
        map.put("CC",89);
        map.put("BB",78);
        map.put("JJ",new Date());
        map.put("MM",78);
//        map.put(67,78); //报错ClassCastException,因为key的类型(Integer)与之前的key的类型(String)不一致
        Set entrySet = map.entrySet();
        for(Object entry : entrySet){
            System.out.println(entry);
        }
    }
    @Test
    public void test2(){
        TreeMap map = new TreeMap();
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",43);
        User u3 = new User("Rose",13);
        User u4 = new User("Jack",23);
        User u5 = new User("Tony",33);
        map.put(u1,78);
        map.put(u2,76);
        map.put(u3,88);
        map.put(u4,45);
        map.put(u5,99);
        Set entrySet = map.entrySet();
        for(Object entry : entrySet){
            System.out.println(entry);
        }
//        System.out.println(map.containsKey(new User("Maria", 33))); //true
    }
    /**
     * 定制排序
     */
    @Test
    public void test3(){
        Comparator comparator = new Comparator(){
            public int compare(Object o1, Object o2) {
                if(o1 instanceof User && o2 instanceof User){
                    User u1 = (User)o1;
                    User u2 = (User)o2;
                    int value = u1.getName().compareTo(u2.getName());
                    if(value != 0){
                        return value;
                    }
                    return u1.getAge() - u2.getAge();
                }
                throw new RuntimeException("类型不匹配");
            }
        };
        TreeMap map = new TreeMap(comparator);
        User u1 = new User("Tom",23);
        User u2 = new User("Jerry",43);
        User u3 = new User("Rose",13);
        User u4 = new User("Jack",23);
        User u5 = new User("Tony",33);
        map.put(u1,78);
        map.put(u2,76);
        map.put(u3,88);
        map.put(u4,45);
        map.put(u5,99);
        Set entrySet = map.entrySet();
        for(Object entry : entrySet){
            System.out.println(entry);
        }
    }
}

 PropertiesTest 

public class PropertiesTest {
    @Test
    public void test1() throws IOException { //注意:因为涉及到流的操作,为了确保流能关闭,建议使用try-catch-finally
        //方式1:数据和代码耦合度高,如果修改的话,需要重写的编译代码、打包发布,繁琐
        //数据
//        String name = "Tom";
//        String password = "abc123";

        //代码:用于操作name,password
        //...

        //方式2:将数据封装到具体的配置文件中,在程序中读取配置文件中的信息,实现了
        //数据和代码的解耦,由于我们没有修改代码,就省去了重新编译和打包的过程
        File file = new File("info.properties"); //注意,要提前创建好
//        System.out.println(file.getAbsolutePath());
        FileInputStream fis = new FileInputStream(file);

        Properties pros = new Properties();
        pros.load(fis); //加载流中的文件中的数据

        //读取数据
        String name = pros.getProperty("name");
        String pwd = pros.getProperty("password");
        System.out.println(name + ":" + pwd); //Tom:abc123
        fis.close();
    }

//    public static void main(String[] args) {
//        File file = new File("info.properties");
//        System.out.println(file.getAbsolutePath());
//    }
}

 User 

public class User implements Comparable{
    private String name;
    private int age;

    public User() {
    }

    public User(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 "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

//    @Override
//    public boolean equals(Object o) {
//        System.out.println("User equals()...");
//        if (o == null || getClass() != o.getClass()) return false;
//        User user = (User) o;
//        return age == user.age && Objects.equals(name, user.name);
//    }
//
//    @Override
//    public int hashCode() {
//        return Objects.hash(name, age);
//    }

    /**
     * 按照年龄的从小到大排序
     * @param o the object to be compared.
     * @return
     */
    @Override
    public int compareTo(@NotNull Object o) {
        if(this == o) {
            return 0;
        }
        if(o instanceof User){
            User u = (User)o;
            return this.age - u.age;
        }
        throw new RuntimeException("类型不匹配");
    }

    /**
     * 比如:先比较年龄从小到大排列,如果年龄相等,则继续比较姓名,从小到大
     */
//    @Override
//    public int compareTo(@NotNull Object o) {
//        if(this == o) {
//            return 0;
//        }
//        if(o instanceof User){
//            User u = (User)o;
//            int value = this.age - u.age;
//            if(value != 0){
//                return value;
//            }
//            return -this.name.compareTo(u.name);
//        }
//        throw new RuntimeException("类型不匹配");
//    }
}

 练习:

添加你喜欢的歌手以及你喜欢他唱过的歌曲,并遍历
public class StringTest {
    public static void main(String[] args) {
        HashMap singers = new HashMap();
        String singer1 = "周杰伦";
        ArrayList songs1 = new ArrayList();
        songs1.add("夜曲");
        songs1.add("晴天");
        songs1.add("七里香");
        songs1.add("发如雪");
        songs1.add("屋顶");
        songs1.add("青花瓷");
        singers.put(singer1,songs1);
        //在添加1个歌手和其歌曲
        String singer2 = "林俊杰";
        ArrayList songs2 = new ArrayList();
        songs2.add("江南");
        songs2.add("曹操");
        songs2.add("小酒窝");
        songs2.add("可惜没如果");
        singers.put(singer2, songs2);
        Set entrySet = singers.entrySet();
        Iterator iterator = entrySet.iterator();
        while(iterator.hasNext()){
            Map.Entry entry = (Map.Entry)iterator.next();
            System.out.println("歌手:" + entry.getKey());
            System.out.println("歌曲有:" + entry.getValue());
        }
    }
}

练习2

 

案例:二级联动
将省份和城市的名称保存在集合中,当用户选择省份以后,二级联动,显示对应省份的地级市供用户选择。
效果演示见课件。
如果输入的城市不正确,需要重新输入。提示:如果输入的省份不正确,需要重新输入。
已有代码:
class CityMap{
    public static Map model=new HashMap();
    static {
        model.put("北京",new string[]{"北京"});
        model.put("辽宁",new string[]{"沈阳","盘锦","铁岭","丹东","大连","锦州""营口"});
        model.put("吉林",new String[]{"长春",“延边”,"吉林",“白山","白城”,"四平","松原"});
        model.put("河北",new string[]{"承德","沧州","邯郸","邢台""唐山","保定","石家庄"});
        model.put("河南", new String[]{"郑州",“许昌",“开封","洛阳",“商丘”,“南阳",“新乡"});
        model.put("山东", new string[]{"济南","青岛",“日照","临沂","泰安","聊城","德州"});
    }
}
public class CityMapTest {
    public static void main(String[] args) {
        //1.获取Map,并遍历map中的所有的key
        Map map = CityMap.model;
        Set provinces = map.keySet();
        for(Object province : provinces){
            System.out.println(province + "\t\t");
        }
        //2.根据提示,从键盘获取省份,判断此省份是否存在,如果存在遍历其value中的各个城市
        //如果不存在,提示用户重新输入
        Scanner scan = new Scanner(System.in);
        String[] cities;
        while(true){
            System.out.println("\n请选择你所在的省份:");
            String province = scan.next();
            //获取省份对应的各个城市构成的String[]
            cities = (String[])map.get(province);
            if(cities == null && cities.length == 0){
                System.out.println("你输入的省份有误,请重新输入");
            }else {
                break; //意味着用户输入的省份是存在的,则跳出当前循环
            }
        }
        for(int i = 0; i < cities.length;i++){
            System.out.println(cities[i] + "\t\t");
        }
        System.out.println();
        //3.根据提示,从键盘获取城市,遍历各个城市构成的String[],判断输入的城市是否存在于此数组中
        //如果存在,信息登记完毕,如果不存在,提示用户重新输入
        l:while(true){
            System.out.println("\n请选择你所在的城市:");
            String city = scan.next();
            //方式1:
//            for(int i = 0;i<cities.length;i++){
//                if(city.equals(cities[i])){
//                    System.out.println("信息登记完毕");
//                    break l;
//                }
//            }
//            System.out.println("输入的城市有误,请重新输入");

            //方式2:
            if(containsCity(cities,city)){
                System.out.println("信息登记完毕");
                break;
            }
            System.out.println("输入的城市有误,请重新输入");
        }
        scan.close();
    }
    //第2种方式处理城市是否存在
    public static boolean containsCity(String[] cities,String city){
        for(int i = 0;i<cities.length;i++){
            if(city.equals(cities[i])){
                System.out.println("信息登记完毕");
                return true;
            }
        }
        return false;
    }
}
class CityMap{
    public static Map model = new HashMap();
    static {
        model.put("北京",new String[]{"北京"});
        model.put("辽宁",new String[]{"沈阳","盘锦","铁岭","丹东","大连","锦州","营口"});
        model.put("吉林",new String[]{"长春","延边","吉林","白山","白城","四平","松原"});
        model.put("河北",new String[]{"承德","沧州","邯郸","邢台","唐山","保定","石家庄"});
        model.put("河南", new String[]{"郑州","许昌","开封","洛阳","商丘","南阳","新乡"});
        model.put("山东", new String[]{"济南","青岛","日照","临沂","泰安","聊城","德州"});
    }
}

 Collections工具类

总结:

1.Collections概述
Collections 是一个操作 Set、List 和 Map 等集合的工具类
2. 常用方法
Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可
变、对集合对象实现同步控制等方法(均为static方法):
排序操作:
reverse(List):反转 List 中元素的顺序
shuffle(List):对 List 集合元素进行随机排序
sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
swap(List,int, int):将指定 list 集合中的i处元素和j处元素进行交换
查找
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素。
object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
Object min(Collection,comparator):根据 Comparator 指定的顺序,返回给定集合中的最小元素
int binarySearch(list list,T key)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且
    必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
int binarySearch(List list,T key,Comparator c)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子
类对象,而且集合也事先必须是按照c比较器规则进行排序过的,否则结果不确定
int frequency(Collection c,Object o):返回指定集合中指定元素的出现次数
复制、替换
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换 List 对象的所有旧值
提供了多个unmodifiableXxx(方法,该方法返回指定 Xxx的不可修改的视图。
添加
boolean addAll(Collection c,T....elements)将所有指定元素添加到指定 collection 中。
同步
Collections 类中提供了多个synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,
从而可以解决多线程并发访问集合时的线程安全问题:


3,面试题:区分Collection和Collections
Collection:集合框架中的用于存储一个一个元素的接口,又分为List和Set等子接口。
Collections:用于操作集合框架的一个工具类。此时的集合框架包括:Set、List、Map

代码:

public class CollectionsTest {
    /**
     * reverse(List):反转 List 中元素的顺序
     * shuffle(List):对 List 集合元素进行随机排序
     * sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
     * sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
     * swap(List,int, int):将指定 list 集合中的i处元素和j处元素进行交换
     */
    @Test
    public void test1(){
        List list = Arrays.asList(45, 43, 65, 6, 43, 2, 32, 45, 56, 34, 23);
        System.out.println(list);
        //reverse(List):反转 List 中元素的顺序
//        Collections.reverse(list);
        //shuffle(List):对 List 集合元素进行随机排序
//        Collections.shuffle(list);
        //sort(List):根据元素的自然顺序对指定 List 集合元素按升序排序
//        Collections.sort(list);
        //sort(List,Comparator):根据指定的 Comparator 产生的顺序对 List 集合元素进行排序
        Collections.sort(list, new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Integer && o2 instanceof Integer){
                    Integer i1 = (Integer)o1;
                    Integer i2 = (Integer)o2;
//                    return i1 - i2;
                    return -(i1.intValue() - i2.intValue()); //拆箱
                }
                throw new RuntimeException("类型不匹配");
            }
        });
        System.out.println(list);
    }
    /**
     * 查找
     * Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
     * Object max(Collection,Comparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素。
     * object min(Collection):根据元素的自然顺序,返回给定集合中的最小元素
     * Object min(Collection,comparator):根据 Comparator 指定的顺序,返回给定集合中的最小元素
     * int binarySearch(list list,T key)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子类对象,而且
     *     必须是可比较大小的,即支持自然排序的。而且集合也事先必须是有序的,否则结果不确定。
     * int binarySearch(List list,T key,Comparator c)在List集合中查找某个元素的下标,但是List的元素必须是T或T的子
     * 类对象,而且集合也事先必须是按照c比较器规则进行排序过的,否则结果不确定
     * int frequency(Collection c,Object o):返回指定集合中指定元素的出现次数
     */
    @Test
    public void test2(){
        List list = Arrays.asList(45, 43, 65, 6, 43, 2, 32, 45, 56, 34, 23);
        System.out.println(list);
        Object max = Collections.max(list);
        Object max1 = Collections.max(list,new Comparator(){
            @Override
            public int compare(Object o1, Object o2) {
                if(o1 instanceof Integer && o2 instanceof Integer){
                    Integer i1 = (Integer)o1;
                    Integer i2 = (Integer)o2;
//                    return i1 - i2;
                    return i1.intValue() - i2.intValue(); //拆箱
                }
                throw new RuntimeException("类型不匹配");
            }
        });
        System.out.println(max+ "\t" +max1); //65
        int count = Collections.frequency(list, 45);
        System.out.println("45出现了" + count + "次");
    }
    @Test
    public void test3(){
        List src = Arrays.asList(45, 43, 65, 6, 43, 2, 32, 45, 56, 34, 23);
        System.out.println(src);
        //void copy(List dest,List src):将src中的内容复制到dest中
        //错误的写法:
//        List dest = new ArrayList();
//        System.out.println(dest.size()); //0
        //正确的写法:
        List dest = Arrays.asList(new Object[src.size()]);
        System.out.println(dest.size()); //11
        Collections.copy(dest,src);
        System.out.println(dest);
    }

    /**
     * 提供了多个unmodifiableXxx(方法,该方法返回指定 Xxx的不可修改的视图。
     */
    @Test
    public void test4(){
        List list1 = new ArrayList();
        //list1可以写入数据
        list1.add(34);
        list1.add(12);
        list1.add(45);
        List list2 = Collections.unmodifiableList(list1);
        //此时的list2只能读,不能写
        list2.add("AA"); //不能写
        System.out.println(list2.get(0));
    }
    @Test
    public void test5(){
        //Collections 类中提供了多个synchronizedXxx() 方法
        List list1 = new ArrayList();
        //返回的list2就是线程安全的
        List list2 = Collections.synchronizedList(list1);
        list2.add(123);
        HashMap map1 = new HashMap();
        //返回的map2就是线程安全的
        Map map2 = Collections.synchronizedMap(map1);
    }
}

练习题(斗地主):

模拟斗地主洗牌和发牌,牌没有排序
效果如图。
提示:不要忘了大王、小王
String[] num = {"A","2","3","4","5","6","7","8","9","J", "Q","K"};
String[] color = {"方片◆","梅花♣","红桃♥","黑桃♠"};
ArrayList poker = new ArrayList();
public class PokerTest {
    public static void main(String[] args) {
        //1.组成一副扑克牌
        String[] num = {"A","2","3","4","5","6","7","8","9","J", "Q","K"};
        String[] color = {"方片◆","梅花♣","红桃♥","黑桃♠"};
        ArrayList poker = new ArrayList();
        for(int i = 0; i < color.length; i++){
            for(int j = 0; j < num.length; j++){
                poker.add(color[i] + "" + num[j]);
            }
        }
        //添加大小王
        poker.add("小王");
        poker.add("大王");
        //2.洗牌
        Collections.shuffle(poker);
        //3.发牌
        //3.1 创建3个角色和1个底牌对应的4个ArrayList
        ArrayList tom = new ArrayList();
        ArrayList jerry = new ArrayList();
        ArrayList me = new ArrayList();
        ArrayList lastCards = new ArrayList();
        for(int i = 0; i < poker.size(); i++){
            if(i>=poker.size() - 3){
                lastCards.add(poker.get(i));
            }else if(i%3 == 0){
                tom.add(poker.get(i));
            }else if(i%3 ==1){
                jerry.add(poker.get(i));
            }else if(i%3 == 2){
                me.add(poker.get(i));
            }
        }
        //3.2遍历显示4个ArrayList
        System.out.println("Tom:");
        System.out.println(tom);
        System.out.println("Jerry:");
        System.out.println(jerry);
        System.out.println("Me:");
        System.out.println(me);
        System.out.println("底牌:");
        System.out.println(lastCards);
    }
}

相关文章:

  • 蚂蚁集团主导的ISO密码学国际标准立项,纳入国产算法
  • AI赋能股票:流通股本与总股本:定义、区别及投资意义解析
  • 【STM32单片机】#4 OLED调试外部中断
  • StarRocks的执行计划和Profile
  • 微软 GraphRAG 项目学习总结
  • ubuntu的ubuntu--vg-ubuntu--lv磁盘扩容
  • 黑盒测试、白盒测试、集成测试和系统测试的区别与联系
  • 在 Windows 中使用 DeepSeek 和 Milvus 搭建个人知识库
  • 新加坡国立大学张阳团队开发第二代RNA结构预测算法,多项基准测试超越SOTA
  • 【橘子大模型】使用LangChain和LLM交互
  • 为AI聊天工具添加一个知识系统 之157: Firstness,Secondness和Thirdness
  • Odoo/OpenERP 和 psql 命令行的快速参考总结
  • 51单片机总结
  • mac m 芯片 动态切换 jdk 版本jdk8.jdk11.jdk17
  • 食品计算—Ingredient-Guided RGB-D Fusion Network for Nutritional Assessment
  • 低代码平台,智慧城市建设的加速器
  • Docker学习--容器生命周期管理相关命令--docker pause/unpause 命令
  • 物联网开发项目:AS608+ESP32S3+Vue构建指纹识别系统(二)——ESP32部分
  • Vue React
  • CSS 列表:实现网页布局的艺术
  • 专业网站建设微信商城开发/短视频搜索seo
  • 临漳手机网站建设/论坛推广网站
  • 陕西城乡住房建设厅网站/雅思培训机构哪家好机构排名
  • 铜陵网站制作公司/怎么申请建立网站
  • 建筑做文本网站/百度seo发帖推广
  • 公司做的网站打开慢/怎么样在百度上推广自己的产品