【java基础系列】15- Java的集合框架

Java的集合框架

1、 集合的概念

  • 概念:对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能(实现了对对象的操作)。
  • 集合与数组的区别
    • 数组长度固定,集合长度不固定。
    • 数组可以存储基本类型和引用类型,集合只能存储引用类型。
  • 位置:java.util.*;

2、 Collection 体系集合

在这里插入图片描述

3、 Collection 接口

3.1 Collection父接口

  • 特点:代表一组任意类型的对象,无序、无下标、不能重复。
  • 方法:
    • boolean add(Object obj) :添加一个对象
    • boolean addAll(Collection c):将一个结合中的所有对象添加到此集合中
    • void clear():清空此集合中的所有对象
    • boolean contains(Object o):检查此集合中是否包含o对象
    • boolean equals(Object o):比较此集合是否与指定对象相等
    • boolean isEmpty():判断此集合是否为空
    • boolean remove(Object o):在此集合中移除o对象
    • int size():返回此集合中的元素个数
    • Object[] toArray():将此集合转换成数组

3.2 案例(一)

package com.goshawk.chapter_02.section_7;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * Collection接口的使用
 * (1)添加元素
 * (2)删除元素
 * (3)遍历元素
 * (4)判断
 */
public class Demo1 {
    public static void main(String[] args) {
        // 创建集合
        Collection collection = new ArrayList();
        // 添加元素
        collection.add("苹果");
        collection.add("香蕉");
        collection.add("榴莲");
        collection.add("西瓜");
        System.out.println("元素个数:"+collection.size());
        System.out.println(collection);
        // 删除元素
        collection.remove("榴莲");
        System.out.println("删除之后:"+collection.size());
        // 遍历
        // 增强for
        for (Object object : collection){
            System.out.println(object);
        }
        // 使用迭代器(迭代器专门用来遍历集合的一种方式)
        // hasNext() 有没有下一个元素
        // next() 获取下一个元素
        // remove() 删除当前元素
        Iterator it = collection.iterator();
        while (it.hasNext()){
            String s = (String) it.next();
            System.out.println(s);
            // collection.remove(s); 不能使用collection删除方法,否则会抛出并发修改异常
            it.remove();
        }
        System.out.println("元素个数:"+collection.size());

        // 判断
        System.out.println(collection.contains("西瓜"));
        System.out.println(collection.isEmpty());
    }
}

3.3 案例(二)

package com.goshawk.chapter_02.section_7;

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

    public Student(){}

    public Student(String name, int age) {
        super();
        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 +
                '}';
    }
}

package com.goshawk.chapter_02.section_7;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

/**
 * Collection的使用:保存学生信息
 */
public class Demo2 {
    public static void main(String[] args) {
        // 创建Collection对象
        Collection collection = new ArrayList();
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 21);
        // 添加数据
        collection.add(s1);
        collection.add(s2);
        System.out.println("元素个数:"+collection.size());
        System.out.println(collection.toString());
        // 删除
        // collection.remove(s1);
        // collection.remove(new Student("李四", 21));
        // collection.clear();
        // System.out.println("删除之后:"+collection.size());

        // 遍历
        // 增强for
        for (Object obejct : collection) {
            Student s = (Student) obejct;
            System.out.println(s.toString());
        }
        // 迭代器 : hasNext() next() remove() ;迭代过程中不能使用collection的删除方法
        Iterator it = collection.iterator();
        while (it.hasNext()){
            Student s = (Student) it.next();
            System.out.println(s.toString());
        }

        // 判断是否存在
        System.out.println(collection.contains(s1));
        System.out.println(collection.isEmpty());
    }
}

4、 List 集合

4.1 List子接口

  • 特点:有序、有下标、元素可以重复
  • 方法:
    • void add(int index, Object o):在index位置插入对象o
    • boolean addAll(int index, Collection c):将一个集合中的元素添加到此集合中的index位置。
    • Object get(int index):返回集合中指定位置的元素
    • List subList(int fromIndex, int toIndex):返回fromIndextoIndex之间的集合元素。
4.1.1 案例(一)
package com.goshawk.chapter_02.section_8;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

/**
 * List子接口的使用
 */
public class Demo1 {
    public static void main(String[] args) {
        // 创建集合对象
        List list = new ArrayList<>();
        // 添加元素
        list.add("苹果");
        list.add("小米");
        list.add("华为");
        System.out.println("元素个数:"+list.size());
        System.out.println(list.toString());
        // 删除元素
        // list.remove("苹果");
        list.remove(0);
        System.out.println("删除之后:"+list.size());
        System.out.println(list.toString());

        // 遍历
        // 使用for遍历
        for (int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        // 增强for
        for (Object object : list) {
            System.out.println(object);
        }

        // 迭代器
        Iterator it = list.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        // 使用列表迭代器,和Iterator的区别:ListIterator可以向前或向后遍历,添加、删除、修改元素
        ListIterator list1 = list.listIterator();
        System.out.println("使用列表迭代器从前往后");
        while (list1.hasNext()){
            System.out.println(list1.nextIndex()+":"+list1.next());
        }
        System.out.println("使用列表迭代器从后往前");
        while (list1.hasPrevious()){
            System.out.println(list1.previousIndex()+":"+list1.previous());
        }

        // 判断
        System.out.println(list.contains("苹果"));
        System.out.println(list.isEmpty());

        // 获取位置
        System.out.println(list.indexOf("华为"));
    }
}

4.1.2 案例(二)
package com.goshawk.chapter_02.section_8;

import java.util.ArrayList;
import java.util.List;

public class Demo2 {
    public static void main(String[] args) {
        // 创建集合
        List list = new ArrayList();
        // 添加数字数据(自动装箱)
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        System.out.println("元素个数:"+list.size());
        System.out.println(list.toString());
        
        // 删除操作
        // list.remove(0);
        // list.remove((Object) 20);
        list.remove(new Integer(20));
        System.out.println("删除之后:"+list.size());
        System.out.println(list.toString());
        
        // subList() 返回子集合,含头不含尾
        List subList = list.subList(1, 3);
        System.out.println(subList.toString());
    }
}

4.2 List 实现类

  • ArrayList
    • 数组结构实现,查询快、增删慢;
    • JDK1.2版本,运行效率快、线程不安全。
  • Vector
    • 数组结构实现,查询快、增删慢;
    • JDK1.0版本,运行效率慢、线程安全。
  • LinkedList
    • 链表结构实现,增删快、查询慢。
4.2.1 ArrayList的使用
4.2.1.1 案例(一)
package com.goshawk.chapter_02.section_7;

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

    public Student(){}

    public Student(String name, int age) {
        super();
        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 +
                '}';
    }

    public boolean equals(Object obj){
        // 判断是不是同一个对象
        if (this==obj){
            return true;
        }
        // 判断是否为空
        if (obj==null){
            return false;
        }
        // 判断是否是Student类型
        if (obj instanceof Student){
            Student s = (Student) obj;
            // 比较属性
            if (this.name.equals(s.getName())&&this.age==s.getAge()){
                return true;
            }
        }
        // 不满足
        return false;
    }
}

package com.goshawk.chapter_02.section_8;

import com.goshawk.chapter_02.section_7.Student;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;

/**
 * ArrayList的使用
 * 存储结构:数组,查找遍历速度快,查询慢
 */
public class Demo3 {
    public static void main(String[] args) {
        // 创建集合
        ArrayList arrayList = new ArrayList<>();
        // 添加元素
        Student s1 = new Student("刘德华", 20);
        Student s2 = new Student("郭富城", 21);
        Student s3 = new Student("梁朝伟", 22);
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        System.out.println("元素个数:"+arrayList.size());
        System.out.println(arrayList.toString());

        // 删除元素
        arrayList.remove(s1);
        /**
         * arrayList.remove(new Student("刘德华", 20));
         * 这种方式为什么删除不掉。因为在进行equals(this==obj)是取地址来进行比较的
         * 如果想要使用这种方式删除,则需要重写equals方法
         */

        System.out.println("删除之后:"+arrayList.size());

        // 遍历元素
        // 使用迭代器
        Iterator it = arrayList.iterator();
        while (it.hasNext()){
            Student s = (Student) it.next();
            System.out.println(s.toString());
        }

        // 列表迭代器
        ListIterator lit = arrayList.listIterator();
        while (lit.hasNext()){
            Student s = (Student) lit.next();
            System.out.println(s.toString());
        }

        // 逆序列表迭代器
        while (lit.hasPrevious()){
            Student s = (Student) lit.previous();
            System.out.println(s.toString());
        }

        // 判断
        System.out.println(arrayList.contains(new Student("梁朝伟",22)));
        System.out.println(arrayList.isEmpty());

        // 查找
        System.out.println(arrayList.indexOf(new Student("梁朝伟", 22)));


    }
}

4.2.1.2 ArrayList源码分析
  • 默认容量:DEFAULT_CAPACITY = 10

    • 如果没有向集合中添加任何元素时,则容量为0;添加一个元素之后,则容量为10;

    • 扩容:每次扩容的大小是原来的1.5倍。

  • 存放元素的数组:elementData

  • 实际元素个数:size

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.2 Vector的使用
4.2.2.1 案例(一)
package com.goshawk.chapter_02.section_9;

import java.util.Enumeration;
import java.util.Vector;

/**
 * Vector集合的使用
 * 存储结构:数组
 */
public class Demo1 {
    public static void main(String[] args) {
        // 创建集合
        Vector vector = new Vector<>();
        // 添加元素
        vector.add("草莓");
        vector.add("芒果");
        vector.add("西瓜");
        System.out.println("元素个数:"+vector.size());

        // 删除
        // vector.remove(0);
        // vector.remove("西瓜");
        // vector.clear();

        // 遍历
        // 使用枚举器
        Enumeration en = vector.elements();
        while (en.hasMoreElements()){
            String o = (String) en.nextElement();
            System.out.println(o);
        }

        // 判断
        System.out.println(vector.contains("西瓜"));
        System.out.println(vector.isEmpty());

        // Vector其他方法
        // firsetElement lastElement ElementAt()
    }
}

4.2.3 LinkedList的使用
4.2.3.1 案例(一)
package com.goshawk.chapter_02.section_7;

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

    public Student(){}

    public Student(String name, int age) {
        super();
        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 +
                '}';
    }

    public boolean equals(Object obj){
        // 判断是不是同一个对象
        if (this==obj){
            return true;
        }
        // 判断是否为空
        if (obj==null){
            return false;
        }
        // 判断是否是Student类型
        if (obj instanceof Student){
            Student s = (Student) obj;
            // 比较属性
            if (this.name.equals(s.getName())&&this.age==s.getAge()){
                return true;
            }
        }
        // 不满足
        return false;
    }
}

package com.goshawk.chapter_02.section_9;

import com.goshawk.chapter_02.section_7.Student;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.ListIterator;

/**
 * LinkedList的使用
 * 存储接口:双向链表
 */
public class Demo2 {
    public static void main(String[] args) {
        // 创建集合
        LinkedList linkedList = new LinkedList<>();
        // 添加元素
        Student s1 = new Student("刘德华", 18);
        Student s2 = new Student("郭富城", 19);
        Student s3 = new Student("梁朝伟", 20);
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        linkedList.add(s3);
        System.out.println("元素个数:"+linkedList.size());
        System.out.println(linkedList.toString());

        // 删除
        // linkedList.remove(s3);
        // linkedList.remove(new Student("刘德华", 18));

        // 遍历
        // for遍历
        for (int i=0;i<linkedList.size();i++){
            System.out.println(linkedList.get(i));
        }

        // 增强for
        for (Object object : linkedList){
            Student s = (Student) object;
            System.out.println(s.toString());
        }

        // 使用迭代器
        Iterator it = linkedList.iterator();
        while (it.hasNext()){
            Student s = (Student) it.next();
            System.out.println(s.toString());
        }
        // 列表迭代器
        ListIterator lit = linkedList.listIterator();
        while (lit.hasNext()){
            Student s = (Student) lit.next();
            System.out.println(s.toString());
        }

        // 判断
        System.out.println(linkedList.contains(s1));
        System.out.println(linkedList.isEmpty());

        // 获取
        System.out.println(linkedList.indexOf(s1));
    }
}

4.2.3.2 LinkedList源码分析
  • size
  • first
  • last

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.4 ArrayListLinkedList的区别
  • 不同结构实现方式

在这里插入图片描述

  • ArrayList:必须开辟连续空间,查询快,增删慢。
  • LinkedList:无需开辟连续空间,查询慢,增删快。

5、 泛型和工具类

5.1 泛型概述

  • Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
  • 常见形式有泛型类、泛型接口、泛型方法。
  • 语法:
    • <T,...>:T称为类型占位符,表示一种引用类型。
  • 优点:
    • 提高代码的复用性
    • 防止类型转换异常,提高 代码的安全性

5.2 泛型类

5.2.1 案例(一)
package com.goshawk.chapter_02.section_10;

import java.security.PublicKey;

/**
 * 泛型类
 * 语法:类名<T>
 * T 是类型占位符,表示一种引用类型,如果编写多个使用逗号隔开
 */
public class MyGeneric<T> {
    // 使用泛型T
    // 创建变量
    T t;

    // 泛型作为方法的参数
    public void show(T t){
        System.out.println(t);
    }

    // 泛型作为方法的返回值
    public T getT(){
        return t;
    }
}

package com.goshawk.chapter_02.section_10;

public class TestMyGeneric {
    public static void main(String[] args) {
        // 使用过泛型类创建对象
        /**
         * 注意:
         * 1、泛型只能使用引用类型
         * 2、不同泛型类型对象之间不能相互复制
         */
        MyGeneric<String> myGeneric = new MyGeneric<>();
        myGeneric.t = "hello";
        myGeneric.show("大家好");
        String string = myGeneric.getT();

        MyGeneric<Integer> myGeneric1 = new MyGeneric<Integer>();
        myGeneric1.t = 100;
        myGeneric1.show(200);
        Integer integer = myGeneric1.getT();

        // MyGeneric<String> myGeneric2 = myGeneric1; 不同泛型类型对象之间不能相互复制,因为myGeneric1上面已经声明了MyGeneric<Integer> myGeneric1
    }
}

5.3 泛型接口

5.3.1 案例(一)
  • 创建一个接口GenericInterface
package com.goshawk.chapter_02.section_10;

/**
 * 泛型接口
 * 语法:接口名<T>
 *     注意:不能泛型静态常量
 * @param <T>
 */
public interface GenericInterface<T> {
    String name = "张三";
    T server(T t);
}

  • 创建接口的实现类GenericInterfaceImp
package com.goshawk.chapter_02.section_10;

public class GenericInterfaceImp implements GenericInterface<String>{
    @Override
    public String server(String t) {
        System.out.println(t);
        return t;
    }
}

  • 创建第二个接口的实现类GenericInterfaceImp1
package com.goshawk.chapter_02.section_10;

public class GenericInterfaceImp1<T> implements GenericInterface<T>{
    @Override
    public T server(T t) {
        System.out.println(t);
        return t;
    }
}

  • 测试实现类
package com.goshawk.chapter_02.section_10;

public class TestGenericInterfaceImp {
    public static void main(String[] args) {
        GenericInterfaceImp imp1 = new GenericInterfaceImp();
        imp1.server("abcd");

        GenericInterfaceImp1<Integer> imp2 = new GenericInterfaceImp1<>();
        imp2.server(1000);
    }
}

5.4 泛型方法

  • 应用:方法不确定传入的参数类型
package com.goshawk.chapter_02.section_10;

/**
 * 泛型方法
 * 语法;<T> 返回值类型
 
 */
public class GenericMethod {
    // 泛型方法
    public <T> void show(T t){
        System.out.println("泛型方法");
    }

    public <T> T show1(T t){
        System.out.println("泛型方法:" + t);
        return t;
    }
}

package com.goshawk.chapter_02.section_10;

public class TestGenericMethod {
    public static void main(String[] args) {
        GenericMethod genericMethod = new GenericMethod();
        // 类型由传入的参数决定
        genericMethod.show("今天天气雨天");
        genericMethod.show(200);
        genericMethod.show(3.14);
    }
}

5.5 泛型集合

  • 概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
  • 特点:
    • 编译时即可检查,而非运行时抛出异常。
    • 访问时,不必类型转换(拆箱)。
    • 不同泛型之间引用不能相互赋值,泛型不存在多态。
5.5.1 案例(一)
  • 无效案例
package com.goshawk.chapter_02.section_10;

import java.util.ArrayList;

/**
 * 泛型集合 - 抛出异常
 */
public class GenericCollection {
    public static void main(String[] args) {
        ArrayList arrayList = new ArrayList<>();
        arrayList.add("xxx");
        arrayList.add("yyy");
        arrayList.add(10);

        for(Object object : arrayList){
            String str = (String) object;
            System.out.println(str);
        }
    }
}

  • 有效案例
package com.goshawk.chapter_02.section_10;

import com.goshawk.chapter_02.section_7.Student;

import java.util.ArrayList;
import java.util.Iterator;

/**
 * 泛型集合
 */
public class GenericCollection {
    public static void main(String[] args) {
        ArrayList<String> arrayList = new ArrayList<String>();
        arrayList.add("xxx");
        arrayList.add("yyy");
        // arrayList.add(10);

        for(String string : arrayList){
            System.out.println(string);
        }

        // 确定使用类型
        ArrayList<Student> arrayList1 = new ArrayList<Student>();
        Student s1 = new Student("刘德华", 20);
        arrayList1.add(s1);

        Iterator<Student> it = arrayList1.iterator();
        while (it.hasNext()){
            Student s = it.next();
            System.out.println(s.toString());
        }
    }
}

6、Set 集合

6.1 Set的使用

package com.goshawk.chapter_02.section_11;

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

/**
 * Set接口的使用
 * 特点:无序、没有下标、不能重复
 */
public class Demo1 {
    public static void main(String[] args) {
        // 创建集合
        Set<String> set = new HashSet<>();
        // 添加数据
        set.add("苹果");
        set.add("华为");
        set.add("小米");

        System.out.println("数据个数:"+set.size());
        System.out.println(set.toString());

        // 删除
        // set.remove("小米");
        // System.out.println(set.toString());

        // 遍历
        // 使用增强for
        for (String string : set){
            System.out.println(string);
        }

        // 使用迭代器
        Iterator<String> it = set.iterator();
        while(it.hasNext()){
            System.out.println(it.next());
        }
        
        // 判断
        System.out.println(set.contains("华为"));
        System.out.println(set.isEmpty());
    }
}

6.2 Set子接口

  • 特点:无序、无下标、元素不可重复。
  • 方法:全部继承自Collection中的方法。

6.3、Set实现类

  • HashSet
    • 基于HashCode实现元素不重复。
    • 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
  • TreeSet
    • 基于排列顺序实现元素不重复。
    • 实现了SortedSet接口,对集合元素自动排列。
    • 元素对象的类型必须实现Comparable接口,指定排序规则。
    • 通过CompareTo方法确定是否为重复元素。
6.3.1 HashSet的使用
6.3.1.1 案例(一)
package com.goshawk.chapter_02.section_11;

import java.util.HashSet;
import java.util.Iterator;

/**
 * HashSet集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 */
public class Demo2 {
    public static void main(String[] args) {
        // 创建集合
        HashSet<String> hashSet = new HashSet<String>();
        // 添加元素
        hashSet.add("刘德华");
        hashSet.add("梁朝伟");
        hashSet.add("郭富城");

        System.out.println("元素个数:"+hashSet.size());
        System.out.println(hashSet.toString());

        // 删除
        // hashSet.remove("刘德华");
        // System.out.println("删除之后:"+hashSet.size());

        // 遍历
        // 增强for
        for (String string : hashSet){
            System.out.println(string);
        }

        // 使用迭代器
        Iterator<String> it = hashSet.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }
        
        // 判断
        System.out.println(hashSet.contains("刘德华"));
        System.out.println(hashSet.isEmpty());
    }
}

6.3.1.2 案例(二)
package com.goshawk.chapter_02.section_11;

import java.util.Objects;

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

    public Person(){

    }

    public Person(String name, int age) {
        super();
        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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /**
     * 第一种重写方式
    @Override
    public int hashCode() {
        int n1 = this.name.hashCode();
        int n2 = this.age;

        return n1+n2;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj){
            return true;
        }
        if (obj==null){
            return false;
        }
        if (obj instanceof Person){
            Person p = (Person) obj;
            if (this.name.equals(p.getName())&&this.age==p.getAge()){
                return true;
            }
        }
        return false;
    }
    */

    // 第二种重写方式
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        if (age != person.age) return false;
        if (name == null){
            if (person.name != null) return false;
        } else if (!name.equals(person.name))
            return false;
        return true;
    }

    @Override
    public int hashCode() {
        // (1)31是一个质数(可以被1和本身整除),尽量减少散列冲突
        // (2)31可以提高执行效率 31*i=(i<<5)-i --> 表示i左移5位,当i=2时即等同于2的6次方
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }
}

package com.goshawk.chapter_02.section_11;
/**
 * HashSet的使用
 *
 * 存储过程(重复依据):
 * (1)根据hashcode计算保存的位置,如果此位置为空,则直接保存,如果不为空执行第二步。
 * (2)再执行equals方法,如果equals方法返回为true,则认为是重复,否则形成链表。
 */

import java.util.HashSet;
import java.util.Iterator;

public class Demo3 {
    public static void main(String[] args) {
        // 创建集合
        HashSet<Person> persons = new HashSet<>();
        // 添加数据
        Person p1 = new Person("刘德华", 18);
        Person p2 = new Person("林志玲", 19);
        Person p3 = new Person("梁朝伟", 20);

        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(new Person("梁朝伟", 20)); // 没有重写equals之前是可以添加重复,重写之后是不能添加重复的 判断名称与年龄一样即表示重复

        System.out.println("元素个数:"+persons.size());
        System.out.println(persons.toString());

        // 删除操作
        // persons.remove(p1);
        // persons.remove(new Person("刘德华", 18)); // 重写equals之前这种方式是不能删除的
        // System.out.println("删除之后:"+persons.size());

        // 遍历
        // 增强for
        for (Person person : persons){
            System.out.println(person.toString());
        }

        // 迭代器
        Iterator<Person> it = persons.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        // 判断
        System.out.println(persons.contains(p1));
        System.out.println(persons.contains(new Person("刘德华", 18))); // 此时这种方式也是可以判断的,因为已经重写了haseCode与equals
        System.out.println(persons.isEmpty());
    }
}

6.3.2 TreeSet的使用
6.3.2.1 案例(一)
package com.goshawk.chapter_02.section_11;

import java.util.Iterator;
import java.util.TreeSet;

/**
 * TreeSet的使用
 * 存储结构:红黑树
 */
public class Demo4 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<String> treeSet = new TreeSet<>();
        // 添加元素
        treeSet.add("xxx");
        treeSet.add("yyy");
        treeSet.add("abc");
        System.out.println("元素个数:"+treeSet.size());
        System.out.println(treeSet.toString());

        // 删除
        treeSet.remove("xxx");
        System.out.println("删除之后:"+treeSet.toString());

        // 遍历
        // 增强for
        for (String string : treeSet){
            System.out.println(string);
        }

        // 迭代器
        Iterator<String> it = treeSet.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        // 判断
        System.out.println(treeSet.contains("abc"));
    }
}

6.3.2.2 案例(二)
package com.goshawk.chapter_02.section_11;

import java.util.Objects;

public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public Person(){

    }

    public Person(String name, int age) {
        super();
        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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    /**
     * 第一种重写方式
    @Override
    public int hashCode() {
        int n1 = this.name.hashCode();
        int n2 = this.age;

        return n1+n2;
    }

    @Override
    public boolean equals(Object obj) {
        if (this==obj){
            return true;
        }
        if (obj==null){
            return false;
        }
        if (obj instanceof Person){
            Person p = (Person) obj;
            if (this.name.equals(p.getName())&&this.age==p.getAge()){
                return true;
            }
        }
        return false;
    }
    */

    // 第二种重写方式
    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        if (age != person.age) return false;
        if (name == null){
            if (person.name != null) return false;
        } else if (!name.equals(person.name))
            return false;
        return true;
    }

    @Override
    public int hashCode() {
        // (1)31是一个质数(可以被1和本身整除),尽量减少散列冲突
        // (2)31可以提高执行效率 31*i=(i<<5)-i --> 表示i左移5位,当i=2时即等同于2的6次方
        final int prime = 31;
        int result = 1;
        result = prime * result + age;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    // 先按姓名比较,再按年龄比较
    @Override
    public int compareTo(Person o) {
        int n1 = this.getName().compareTo(o.getName());
        int n2 = this.age-o.getAge();
        return n1==0?n2:n1;
    }
}

package com.goshawk.chapter_02.section_11;

import java.util.Iterator;
import java.util.TreeSet;

/**
 * 使用TreeSet保存数据
 * 存储结构:红黑数
 * 要求:元素必须要实现Comparable接口,compareTo()方法返回值为0,认为是重复元素
 */
public class Demo5 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<Person> peoples = new TreeSet<>();
        // 添加元素
        Person p1 = new Person("刘德华", 18);
        Person p2 = new Person("梁朝伟", 19);
        Person p3 = new Person("林志玲", 20);

        peoples.add(p1);
        peoples.add(p2);
        peoples.add(p3);
        System.out.println("元素个数:"+peoples.size());
        System.out.println(peoples.toString());

        // 删除
        peoples.remove(p1);
        System.out.println("删除之后:"+peoples.size());

        // 遍历
        // 增强for
        for (Person person : peoples){
            System.out.println(person.toString());
        }

        // 迭代器
        Iterator<Person> it = peoples.iterator();
        while (it.hasNext()){
            System.out.println(it.next());
        }

        // 判断
        System.out.println(peoples.contains(p1));
        System.out.println(peoples.contains(new Person("刘德华", 18)));
    }
}

6.3.2.3 案例(三)–> Comparator 接口
package com.goshawk.chapter_02.section_11;

import java.util.Objects;

public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public Person(){

    }

    public Person(String name, int age) {
        super();
        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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        if (age != person.age) return false;
        if (name == null){
            if (person.name != null) return false;
        } else if (!name.equals(person.name))
            return false;
        return true;
    }

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

    // 先按姓名比较,再按年龄比较
    @Override
    public int compareTo(Person o) {
        int n1 = this.getName().compareTo(o.getName());
        int n2 = this.age-o.getAge();
        return n1==0?n2:n1;
    }
}

package com.goshawk.chapter_02.section_11;

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

/**
 * TreeSet集合的使用
 * Comparator:实现定制比较(比较器)
 * Comparable:可比较的
 */
public class Demo6 {
    public static void main(String[] args) {
        // 创建集合,并指定比较规则
        TreeSet<Person> peoples = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                int n1 = o1.getAge() - o2.getAge();
                int n2 = o1.getName().compareTo(o2.getName());
                return n1 == 0 ? n2 : n1;
            }
        });

        Person p1 = new Person("xyz", 18);
        Person p2 = new Person("abc", 19);
        Person p3 = new Person("zhangsan", 20);
        Person p4 = new Person("lisi", 20);

        peoples.add(p1);
        peoples.add(p2);
        peoples.add(p3);
        peoples.add(p4);

        System.out.println(peoples.toString());
    }
}

6.3.2.4 案例(四)
package com.goshawk.chapter_02.section_11;

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

/**
 * 要求:使用TreeSet结合实现字符串按照长度进行排序
 * helloword zhangsan lisi  wangwu  beijing shanghai  nanjing xian
 * Comparator接口实现定制比较
 */
public class Demo7 {
    public static void main(String[] args) {
        // 创建集合,并指定比较规则
        TreeSet<String> treeSet = new TreeSet<>(new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                int n1 = o1.length()-o2.length();
                int n2 = o1.compareTo(o2);

                return n1 == 0?n2:n1;
            }
        });
        // 添加数据
        treeSet.add("helloword");
        treeSet.add("zhangsan");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        treeSet.add("beijing");
        treeSet.add("nanjing");
        treeSet.add("shanghai");
        treeSet.add("xian");

        System.out.println(treeSet.toString());
    }
}

7、Map 体系集合

在这里插入图片描述

7.1 Map父接口

  • 特点:存储一对数据(Key-Value),无序、无下标,键不可重复,值可以重复。
  • 方法:
    • V put(K key, V value):将对象存入到集合中,关联键值。key重复则覆盖原值。
    • Object get(Object key):根据键获取对应的值。
    • Set<K>:返回所有key
    • Collection<V> values():返回包含所有值的Collection集合。
    • Set<Map.Entry<K, V>:键值匹配的Set集合。
7.1.1 案例(一)

在这里插入图片描述

package com.goshawk.chapter_02.section_12;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Map接口的使用
 * 特点:
 * (1)存储键值对
 * (2)键不能重复,值可以重复
 * (3)无序
 */
public class Demo1 {
    public static void main(String[] args) {
        // 创建Map集合
        Map<String, String> map = new HashMap<>();
        // 添加元素
        map.put("cn", "中国");
        map.put("uk", "英国");
        map.put("usa", "美国");
        map.put("cn", "zhongguo"); // 添加重复,会替换之前的值

        System.out.println("元素个数:"+map.size());
        System.out.println(map.toString());

        // 删除
        // map.remove("usa");
        // System.out.println("删除之后:"+map.size());

        // 遍历
        // 使用keySet()

        for (String key : map.keySet()){
            System.out.println(key+": "+map.get(key));
        }

         // 等同于上方的代码
         Set<String> keyset = map.keySet();
         for (String key : keyset){
         System.out.println(key+": "+map.get(key));
         }


        // 使用entrySet()
        Set<Map.Entry<String, String>> entries = map.entrySet();
        for (Map.Entry<String, String> entry : entries){
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        // 等同于上方的代码
        for (Map.Entry<String, String> entry : map.entrySet()){
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }
        
        // 判断
        System.out.println(map.containsKey("cn"));
        System.out.println(map.containsValue("泰国"));

    }
}

7.2 Map集合的实现类

  • HashMap
    • JDK1.2版本,线程不安全,运行效率快;允许用null作为key或者value
  • Hashtable
    • JDK1.0版本,线程安全,运行效率慢;不允许null作为key或是value
  • Properties
    • Hashtable的子类,要求keyvalue都是String。通常用于配置文件的读取。
  • TreeMap
    • 实现了SortrfMap接口(是Map的子接口),可以对key自动排序。
7.2.1 HashMap的使用
7.2.1.1 案例(一)
package com.goshawk.chapter_02.section_12;

import java.util.Objects;

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

    public Student(){

    }

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

    public String getName() {
        return name;
    }

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

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return stuNo == student.stuNo && Objects.equals(name, student.name);
    }

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

package com.goshawk.chapter_02.section_12;

import java.util.HashMap;
import java.util.Map;

/**
 * HashMap集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 *
 * 使用key的hashCode()和equals()作为重复依据
 */
public class Demo2 {
    public static void main(String[] args) {
        // 创建集合
        HashMap<Student, String> students = new HashMap<Student, String>();
        // 添加元素
        Student s1 = new Student("孙一", 100);
        Student s2 = new Student("孙二", 101);
        Student s3 = new Student("孙三", 102);
        students.put(s1, "北京");
        students.put(s2, "上海");
        students.put(s3, "杭州");
        // students.put(s3. "南京"); 重复
        students.put(new Student("孙三", 102), "杭州");
        System.out.println("元素个数:"+students.size());
        System.out.println(students.toString());

        // 删除
        students.remove(s1);
        System.out.println("删除之后:"+students.size());

        // 遍历
        // 使用keySet() 进行遍历
        for (Student key : students.keySet()){
            System.out.println(key.toString() + ": " + students.get(key));
        }

        // 使用entrySet() 进行遍历
        for (Map.Entry<Student, String> entry : students.entrySet()){
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 判断
        System.out.println(students.containsKey(s1));
        System.out.println(students.containsKey(new Student("孙一", 100)));
        System.out.println(students.containsValue("杭州"));
    }
}

7.2.2 HashMap 源码分析
  • hashMap默认的初始容量大小:16

  • hashMap的数组最大容量大小:2的30次方

  • 默认加载因子:0.75f ,大于这个值说明需要扩容。

  • 树的阈值:TREEIFY_THRESHOLD = 8jdk1.8 链表的长度大于8时,并且数组的长度大于64,就会把链表调整成红黑树。

  • 不可恢复阈值:UNTREEIFY_THRESHOLD = 6jdk1.8 当链表长度小于6时,调整为链表。

  • 最小的容量大小:MIN_TREEIFY_CAPACITY = 64jdk1.8 当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树。

  • transient Node<K, V>[] table;:哈希表中的数组。

  • size:元素个数。

  • 无参构造

public HashMap() {
        this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
    }
// 创建集合
HashMap<Student, String> students = new HashMap<Student, String>();
        
// 刚创建hashmap之后没有添加元素table=null size=0;目的是为了节省空间
  • 添加元素
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }


final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        if ((tab = table) == null || (n = tab.length) == 0)
            n = (tab = resize()).length;
        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);
        else {
            Node<K,V> e; K k;
            if (p.hash == hash &&
                ((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            else {
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            if (e != null) { // existing mapping for key
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)
                    e.value = value;
                afterNodeAccess(e);
                return oldValue;
            }
        }
        ++modCount;
        if (++size > threshold)
            resize();
        afterNodeInsertion(evict);
        return null;
    }

(1)HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table容量调整为16.

(2)当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数。

(3)jdk1.8 当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,目的提高执行效率。

(4)jdk1.8 当链表长度小于6时,调整成链表。

(5)jdk1.8以前,链表是头插入,jdk1.8以后是尾插入。

7.2.3 TreeMap 的使用
7.2.3.1 案例(一)
package com.goshawk.chapter_02.section_12;

import java.util.Objects;

public class Student implements Comparable<Student>{
    private String name;
    private int stuNo;

    public Student(){

    }

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

    public String getName() {
        return name;
    }

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

    public int getStuNo() {
        return stuNo;
    }

    public void setStuNo(int stuNo) {
        this.stuNo = stuNo;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;
        Student student = (Student) o;
        return stuNo == student.stuNo && Objects.equals(name, student.name);
    }

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

    @Override
    public int compareTo(Student o) {
        int n2 = this.stuNo-o.getStuNo();
        return n2;

    }
}

package com.goshawk.chapter_02.section_12;

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;

/**
 * TreeMap的使用
 * 存储结构:红黑树
 */
public class Demo3 {
    public static void main(String[] args) {
        // 创建集合
        TreeMap<Student, String> treeMap = new TreeMap<Student, String>();

        /**
         * 定制比较
        TreeMap<Student, String> treeMap1 = new TreeMap<Student, String>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return 0;
            }
        });
     */

        // 添加元素
        Student s1 = new Student("孙一", 100);
        Student s2 = new Student("孙二", 101);
        Student s3 = new Student("孙三", 102);
        treeMap.put(s1, "北京");
        treeMap.put(s2, "上海");
        treeMap.put(s3, "深圳");
        treeMap.put(new Student("孙三", 102), "南京"); // 替换

        System.out.println("元素个数:"+treeMap.size());
        System.out.println(treeMap.toString());

        // 删除
        // treeMap.remove(s3);
        // treeMap.remove(new Student("孙二", 101));
        // System.out.println("删除之后:"+treeMap.size());

        // 遍历
        // 使用keySet()进行遍历
        for (Student key : treeMap.keySet()){
            System.out.println(key + ": " + treeMap.get(key));
        }

        // 使用entrySet()进行遍历
        for (Map.Entry<Student, String> entry : treeMap.entrySet()){
            System.out.println(entry.getKey() + ": " + entry.getValue());
        }

        // 判断
        System.out.println(treeMap.containsKey(new Student("孙三", 102)));

    }
}

8、Collections 工具类

  • 概念:集合工具类,定义了除了存取以外的集合常用方法。
  • 方法:
    • public static void reverse(List<?> list):反转集合中元素的顺序。
    • public static void shuffle(List<?> list):随机重置集合元素的顺序。
    • public static void sort(List<T>, list):升序排序(元素类型必须实现Comparable接口)。

8.1 案例(一)

package com.goshawk.chapter_02.section_12;

import java.util.*;

/**
 * Collections 工具类的使用
 */
public class Demo4 {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(20);
        list.add(5);
        list.add(13);
        list.add(56);
        list.add(4);
        // sort() 排序
        System.out.println("排序之前:"+list.toString());
        Collections.sort(list);
        System.out.println("排序之后:"+list.toString());

        // binarySearch
        int i = Collections.binarySearch(list, 14); // 返回负数 表示没有找到
        System.out.println(i);

        // copy() 复制
        List<Integer> dest = new ArrayList<>();
        for (int k=0;k<list.size();k++){
            dest.add(0);
        }
        Collections.copy(dest, list);
        System.out.println(dest.toString());

        // reverse() 反转
        Collections.reverse(list);
        System.out.println("反转之后:"+list);

        // shuffle 打乱
        Collections.shuffle(list);
        System.out.println("打乱之后:"+list);

        // list转成数组
        Integer[] arr = list.toArray(new Integer[10]);
        System.out.println(arr.length);
        System.out.println(Arrays.toString(arr));

        // 数组转成集合
        String[] names = {"张三", "李四", "王武"};
        // 这个集合是一个受限集合,不能添加和删除
        List<String> list2 = Arrays.asList(names);
        
        System.out.println(list2);
        
        // 把基本类型数组转成集合时,需要修改为包装类型
        // int[] nums1 = {100, 200, 300, 400, 500};
        // List<int[]> list3 = Arrays.asList(nums1); 类型不只是数字
        Integer[] nums = {100, 200, 300, 400, 500};
        List<Integer> list4 = Arrays.asList(nums);
        System.out.println(list4);
    }
}

9、集合总结

  • 集合的概念
    • 对象的容器,和数组类似,定义了对多个对象进行操作的常用方法。
  • List集合
    • 有序、有下标、元素可以重复。(ArrayList、LinkedList、Vector
  • Set集合
    • 无序、无下标、元素不可重复。(HashSet、TreeSet
  • Map集合
    • 存储一对数据,无序、无下标、键不可重复,值可以重复。(HashMap、HashTable、TreeMap
  • Collections
    • 集合工具类,定义了除了存取以外的集合常用方法。

版权声明:本文为weixin_40960688原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。