Java中的集合 - Faraj

Java中的集合 - Faraj

Java中的集合

集合就像是一个购物车,可以将购买的所有商品的存放在一个统一的购物车中

集合的概念

  • 现实生活: 很多的事物凑在一起

  • 数学中的集合: 具有共同属性的事物的总体

  • Java中的集合类: 是一种工具类,是一种容器,里面可以存储任意数量的相同属性的类。

集合的作用

  • 在类的内部对数据进行组织

  • 简单快速的搜索大数量的条目

  • 有的集合借口,提供了一系列排列有序的元素,并且可以在序列中快速地插入或者删除元素

  • 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这 个关键字可以是任意类型

两个集合框架

  • Collection

    • List
    • Queue
    • Set

    List、Queue、Set的区别:

    List和Queue中的元素是有序排列的

    Set中的元素是无序排列的

    graph TD Collection(Collection) --> List(List) List(List) -.-> ArrayList[ArrayList] List(List) -.-> LinkedList[LinkedList] Collection(Collection) --> Queue(Queue) Queue(Queue)-.-> LinkedList[LinkedList] Collection(Collection) --> Set(Set) Set(Set) -.-> HashSet[HashSet] style ArrayList fill:#F4493F,color:#fff style HashSet fill:#F4493F,color:#fff style List fill:#00A9D4,color:#fff style Set fill:#00A9D4,color:#fff

    在Collection中存储的是一个一个独立的对象

  • Map

graph TD subgraph main Map(Map) --> HashMap end Map(Map)-.键值对.-> Entry(Key,Value) style HashMap fill: #FF3741,color: #FFF

在Map中会以<Key,Value>,也就是两个对象形成一个映射的关系的方式去存储数据,这有点像C#中的Diectory类型。

Collection 接口、子接口及其实现类

JDK API

List接口及其实现类 --- ArrayList

  • List中的元素可以是重复的并且是有序排列的,被称为序列
  • List可以精确的控制每个元素的插入位置,或删除某个位置元素
  • ArrayList --- 数组序列,是List的一个重要实现类
  • ArrayList的底层是由数组实现的

实现功能 --- 模拟学生选课功能

  • 选择课程(往集合中添加课程)
  • 删除所选的某门课程(删除集合中的元素)
  • 查看所选课程
  • 修改所选课程
//: Course.java
package com.faraj.collection;

public class Course {
    public String name;
    public String id;

    public Course(String id, String name) {
        this.id = id;
        this.name = name;
    }
}

创建课程类Course.java 由于是学习,类中的属性就用了public实际应用应该使用private修饰,然后添加getName; setName; getId; SetId方法;

//: Student.java
package com.faraj.collection;

import java.util.HashSet;
import java.util.Set;

public class Student {
    public String id;
    public String name;
    public Set<Course> courses;

    public Student(String id, String name){
        this.id = id;
        this.name = name;
        this.courses = new HashSet<Course>();
    }
}


创建学生类Student.java

//: ListTest.java
package com.faraj.collection;

import java.util.*;

public class ListTest {
    public List<Course> coursesToSelect;

    public ListTest() {
        this.coursesToSelect = new ArrayList<Course>();
    }

    /**
     * 给courseToSelect添加元素
     */
    public void addTest() {
        Course cr1 = new Course("1", "数据库系统概论");
        coursesToSelect.add(cr1);
        Course temp = coursesToSelect.get(0);
        System.out.println("已添加课程: " + temp.id + "-" + temp.name);

        coursesToSelect.add(0, new Course("2", "数据结构"));
        Course temp2 = coursesToSelect.get(0);
        System.out.println("已添加课程: " + temp2.id + "-" + temp2.name);
        coursesToSelect.add(0, new Course("2", "数据结构"));
        Course[] courses = new Course[]{
                new Course("3", "线性代数"),
                new Course("4", "ASP网页设计")
        };
        coursesToSelect.addAll(Arrays.asList(courses));
        System.out.println("已添加课程: " + coursesToSelect.get(2).id + "-" +
                           coursesToSelect.get(2).name);
        System.out.println("已添加课程: " + coursesToSelect.get(3).id + "-" +
                           coursesToSelect.get(3).name);

        coursesToSelect.addAll(2, Arrays.asList(
                new Course("5", "面向对象编程"),
                new Course("6", "汇编语言")));
        System.out.println("已添加课程: " + coursesToSelect.get(2).id + "-" + 
                           coursesToSelect.get(2).name);
        System.out.println("已添加课程: " + coursesToSelect.get(3).id + "-" +
                           coursesToSelect.get(3).name);
    }

    /**
     * 获取集合中的所有元素
     */
    public void getTest() {
        for (int i = 0; i < coursesToSelect.size(); i++) {
            System.out.println("课程:" + coursesToSelect.get(i).id + " - " +
                               coursesToSelect.get(i).name);
        }
    }

    /**
     * 通过迭代器遍历List
     */
    public void iteratorTest() {
        Iterator<Course> it = coursesToSelect.iterator();
        int i = 1;
        while (it.hasNext()) {
            Course cr = it.next();
            System.out.println("课程" + i++ + ":" + cr.id + "-" + cr.name);
        }
    }

    /**
     * 使用foreach方式循环获得list中的所有元素
     */
    public void foreachTest() {
        int i = 1;
        for (Course cr : coursesToSelect) {
            System.out.println("课程" + i++ + ":" + cr.id + "-" + cr.name);
        }
    }

    /**
     * 修改List中的元素
     */
    public void modifyTest() {
        coursesToSelect.set(1, new Course("7", "Java语言设计"));
    }

    /**
     * 删除List中的元素
     */
    public void removeTest(){
        coursesToSelect.remove(0);
    }

    public static void main(String[] args) {
        ListTest lt = new ListTest();
        lt.addTest();
//        lt.getTest();
//        lt.iteratorTest();
        lt.modifyTest();
        lt.removeTest();
        lt.foreachTest();
    }
}


创建备选课程类ListTest.java

package com.faraj.collection;

import java.util.*;

public class SetTest {
    public List<Course> coursesToSelect;

    public SetTest() {
        coursesToSelect = new ArrayList<Course>();
    }

    public void add() {
        coursesToSelect.addAll(
                Arrays.asList(
                        new Course("1", "数据库系统概论"),
                        new Course("2", "C语言基础"),
                        new Course("3", "Python语言实践"),
                        new Course("4", "基础算法实现"),
                        new Course("5", "汇编语言"),
                        new Course("6", "Linux基础"),
                        new Course("7", "面向对象编程"))
        );
    }

    public void getAll() {
        for (Course c : coursesToSelect) {
            System.out.println(c.id + "--" + c.name);
        }
    }

    public static void main(String[] args) {
        SetTest st = new SetTest();
        st.add();
        st.getAll();
        Student stu = new Student("1", "Faraj");
        Scanner sca = new Scanner(System.in);
        System.out.println("Welcome Stu. " + stu.name + "to select courses. You should and only can select 3 course.");
        int num = 1;
        for (int i = 0; i < 3; i++) {
            System.out.println("Please choose NO. " + num + " course"s ID");
            num ++;
            String input = sca.next();
            for (Course cor : st.coursesToSelect) {
                if (cor.id.equals(input)) {
                    stu.courses.add(cor);
                }
            }
        }
        System.out.println("你选择了:");
        for (Course stuC : stu.courses) {
            System.out.println(stuC.id + " - " + stuC.name);
        }
    }

}

Map和HashMap

Map接口

  • Map提供了一种映射关系,其中的元素是以键值()对的形式存储的,能够实现根据key查找value
  • Map中的键值对是以Entry类型的对象实例形式存在
  • key不可重复,但是value值可以重复
  • 每个key只能映射一个值
graph TD key(key) -.-> value(value) key2(key) -.-> value(value) key3(key) -.-> value(value) key4(key...) -.-> value(value) style value fill:#FF2B45,color:white style key fill:#B1CBE5,color:white style key2 fill:#B1CBE5,color:white style key3 fill:#B1CBE5,color:white style key4 fill:#B1CBE5,color:white
  • Map 支持范性,Map<K,V>

HashMap类

  • HashMap是Map的一个重要实现类,也是最常用的,基于哈希表实现
  • HashMap中的Entry对象是无序排列的
  • Key和Value的值都可以为null, 但是一个HashMap中只能有一个Key值为null的映射(Key值不可重复)
案例:

功能说明: 通过Map<String,Student>进行学生信息管理

​ 其中key为学生的ID,value为学生对象

​ 通过键盘输入学生信息

​ 对集合中的学生信息进行增删改查

创建MapTest.java

//: MapTest.java
package com.faraj.collection;

import java.util.*;

public class MapTest {
    public Map<String, Student> students;

    public MapTest() {
        students = new HashMap<>();
    }

    /**
     * 通过put方法在Map中添加entry(键值对)
     */
    public void putTest() {
        Scanner scan = new Scanner(System.in);
        while (true) {
            System.out.println("请输入学生ID");
            String id = scan.next();
            int nextInput;
            if (students.get(id) == null) {
                System.out.println("请输入学生的姓名");
                students.put(id, new Student(id, scan.next()));
                System.out.println("是否继续添加? 1继续 2退出");
                nextInput = scan.nextInt();
                if (nextInput != 1) {
                    if (nextInput == 2) break;
                }
            } else {
                System.out.println("该ID已存在");
                System.out.println("是否重新添加 1是 2退出");
                while (true) {
                    try {
                        nextInput = scan.nextInt();
                        break;
                    } catch (InputMismatchException e) {
                        System.out.println("你输入的内容不合法,请重新输入");

                    }
                }
                if (nextInput != 1) {
                    if (nextInput == 2) break;
                }

            }
        }
    }

    /**
     * 通过keySet取得遍历students
     */
    public void keySetTest() {
        Set<String> keys = students.keySet();
        System.out.println("现已添加学生人数:" + keys.size());
        for (String stuId : keys) {
            System.out.println("学生(" + stuId + ")" + students.get(stuId).name);
        }
    }

    /**
     * 使用entrySet遍历students
     */
    public void entrySetTest() {
        Set<Map.Entry<String, Student>> entries = students.entrySet();
        System.out.println("学生总数:" + entries.size());
        for (Map.Entry<String, Student> entry : entries) {
            System.out.println(entry.getKey() + " - " + entry.getValue().name);
        }
    }

    /**
     * 从Map中删除学生
     */
    public void removeTest() {
        Scanner scan = new Scanner(System.in);
        while (true) {
            System.out.println("请输入要删除的学生ID");
            String id = scan.next();
            if (students.get(id) == null) {
                System.out.println("找不到该学生ID");
                System.out.println("重新输入请输入1,退出应输入2");
                int next = scan.nextInt();
                if (next != 1) {
                    if (next == 2) {
                        break;
                    }
                }
            } else {
                System.out.println("学生(" + id + ")" + students.get(id).name + " 已被删除");
                students.remove(id);
                System.out.println("重新输入请输入1,退出应输入2");
                int next = scan.nextInt();
                if (next != 1) {
                    if (next == 2) {
                        break;
                    }
                }
            }
        }
    }

    /**
     * 通过Put方法更改学生姓名
     */
    public void modifyTest() {
        Scanner scan = new Scanner(System.in);
        while (true) {
            System.out.println("请输入要修改的学生的ID");
            String stuId = scan.next();
            Student stu = students.get(stuId);
            if (stu == null) {
                System.out.println("该ID的学生不存在,请重新输入");
                continue;
            }
            System.out.println("当前学生(" + stuId + ")" + " - " + stu.name + ", 请输入修改后的名字");
            String newName = scan.next();
            Student newStu = new Student(stuId,newName);
            students.put(stuId,newStu);
            break;
        }
    }

    public static void main(String[] args) {
        MapTest mt = new MapTest();
        mt.putTest();
        mt.keySetTest();
        mt.modifyTest();
        mt.entrySetTest();
    }
}

判断coursesToSelect(List)中是否存在课程

在Course.java中重写equals方法

@Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (!(obj instanceof Course)){
            return false;
        }
        Course cour = (Course)obj;
        if (cour.name == null){
            return this.name == null;
        }else{
            return this.name.equals(cour.name);
        }
    }

在SetTest.java类中添加以下方法

    public void containsTest() {
        System.out.println("请输入你要查找的名称");
        Course inputCourse = new Course();
        inputCourse.name = scan.next();
        if (coursesToSelect.contains(inputCourse))
            System.out.println("包含《" + inputCourse.name + "》");
        else System.out.println("不包含《" + inputCourse.name + "》");
    }

在SetTest.java的main方法中测试该方法

st.containsTest();

另外在List中还有一个containsAll()方法,其用法与contains差不多,就是传入的是一个比较的对象集合

判断Set中是否存在课程

将之前SetTest.java中的部分代码提取成一个单独的方法

 public void createStuAndSelectCourse(){
        Student stu = new Student("1", "Faraj");
        System.out.println("Welcome Stu. " + stu.name + "to select courses. You should and only can select 3 course.");
        int num = 1;
        for (int i = 0; i < 3; i++) {
            System.out.println("Please choose NO. " + num + " course"s ID");
            num++;
            String input = scan.next();
            for (Course cor : coursesToSelect) {
                if (cor.id.equals(input)) {
                    stu.courses.add(cor);
                }
            }
        }
        System.out.println("你选择了:");
        for (Course stuC : stu.courses) {
            System.out.println(stuC.id + " - " + stuC.name);
        }
    }

在该文件中创建一下方法

    public void setContainsTest() {
        System.out.println("请输入学生选则的课程名称");
        String name = scan.next();
        Course inputCourse = new Course();
        inputCourse.name = name;
        if (student.courses.contains(inputCourse)) {
            System.out.println("该学生选择了《" + inputCourse.name + "》");
        } else {
            System.out.println("该学生并没有选择《" + inputCourse.name + "》");
        }
    }

在main方法中调用新创建的两个方法

        st.createStuAndSelectCourse();
        st.setContainsTest();

运行程序发现HashSet中的contains方法告诉我们并没有包含课程

不用担心,这不是因为我们的代码写错了,而是……

HashSet的contains方法的实现机制

在Object这个根类中除了定义了上面通过Course类重写的equals()方法,还定义了一个**hashCode()**方法,这个方法返回的是对象的哈希码的值,当我们在调用HashSet的contains()方法的时候,其实先调用每个元素的hashCode获取它们的哈希码,如果哈希码相等的情况下,才会调用equals方法来判断是否相等,这有当这两个方法返回的值都是true的时候,contains()方法才会认定这个HashSet包含某个元素。

graph LR co>HashSet.contains] ==> hc{.hashCode} hc{比较.hashCode} -.相等.-> eq(.equals) hc{比较.hashCode} -. 不相等 .-> false[false] eq(.equals) -.true.-> True[True] eq(.equals) -.false.-> false[false] style co fill:#FFBB4A,color:white,stroke-width:0px style hc fill:#3FBA69,color:white,stroke-width:0px style eq fill:#3FBA69,color:white,stroke-width:0px style True fill:#069BF9,color:white,stroke-width:0px style false fill:#EF3C41,color:white,stroke-width:0px

在Course类中添加hashCode方法的重写

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

image-20200129000227729

然后重新启动程序,就能看到程序正常运行了