Android基础Java 基础

Java 基础 08. Java 集合框架

2022-01-03  本文已影响0人  yjtuuige

一、集合概述

二、Collection 体系集合

三、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() 将集合转换成数组
package com.base.demo09;

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

/*
 * Collection 接口的使用
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        Collection collection = new ArrayList();
        // 1.添加元素
        collection.add("苹果");
        collection.add("梨");
        collection.add("西瓜");
        System.out.println("元素个数:" + collection.size());
        System.out.println(collection); // collection 已自动重写了 toString()方法
        // 2.删除元素
//        collection.remove("西瓜");
        // 清空
//        collection.clear();
        System.out.println("删除后元素个数:" + collection.size());

        // 3.遍历元素(重点)
        // 3.1增强for(不能使用for,因为没有下标)
        System.out.println("--------------3.1增强for-------------");
        for (Object o : collection) {
            System.out.println(o);
        }
        // 3.2使用迭代器(专门用来遍历集合的一种方式,是一个接口)
        // hasNext();   是否有下一个元素
        // next();  获取下一个元素
        // remove();    删除当前元素
        System.out.println("--------------3.2使用迭代器-------------");
        Iterator it = collection.iterator();    // 接口
        while (it.hasNext()) {
            String s = (String) it.next();  // 强转到String
            System.out.println(s);
            // collection.remove(s); 引发错误:并发修改异常。迭代时,不能使用 collection.remove(s)
//            it.remove();    // 可以通过迭代内部方法,进行删除
        }
        System.out.println("元素个数:" + collection.size());
        // 4.判断
        // contains()是否包含
        System.out.println(collection.contains("西瓜"));
        // 是否为空
        System.out.println(collection.isEmpty());
    }
}
package com.base.demo09;

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

// Collection 的使用:保存学生信息
public class Demo02 {
    public static void main(String[] args) {
        // 1.创建 Collection 对象
        Collection collection = new ArrayList();
        // 创建学生对象
        Student s1 = new Student("张三", 20);
        Student s2 = new Student("李四", 19);
        Student s3 = new Student("王五", 21);
        // 2.添加数据
        collection.add(s1);
        collection.add(s2);
        collection.add(s3);
        System.out.println("元素个数:" + collection.size());
        System.out.println(collection.toString());
        // 3.删除
//        collection.remove(s1);
//        collection.remove(new Student("李四", 21));  需重写 equals(this==obj) 方法,才能实现
        // 从集合中删除,并没有删除对象
//        collection.clear();
        System.out.println("删除之后:" + collection.size());

        // 4.遍历
        // 4.1增强 for
        // 遍历后为对象类型,需要转换
        System.out.println("-----------4.1增强 for------------");
        for (Object o : collection) {
            Student s = (Student) o;  // 将对象类型,转换为学生类型
            System.out.println(s.toString());
        }
        // 4.2迭代器 hasNext(); next(); remove(); 迭代过程不能使用 collection 的删除方法
        System.out.println("-----------4.2迭代器------------");
        Iterator it = collection.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();    // 类型转换
            System.out.println(s);
        }
        // 5. 判断
        // 是否包含对象
        System.out.println(collection.contains(s1));
        // 是否为空
        System.out.println(collection.isEmpty());
    }
}
package com.base.demo09;

// 学生类
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 +
                '}';
    }
}

四、Collection 子接口

List 集合

方法名 说明
void add(int index,Object o) 在 index 位置插入对象 o
boolean addAll(index,Collection c) 将一个集合中的元素,添加到此集合中的 index 位置
Object get(int index) 返回集合中,指定位置的元素
List subList(int fromIndex,int toIndex) 返回 fromIndex 和 toIndex 之间的集合元素
package com.base.demo09;

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

/*
 * List 子接口使用
 * 特点:1.有序 2.有下标 3.可以重复
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素
 * 4.判断
 * 5.获取位置
 * */
public class Demo03 {
    public static void main(String[] args) {
        // 创建对象
        List list = new ArrayList<>();
        // 1.添加元素
        list.add("一");
        list.add("二");
        list.add("三");
        System.out.println("元素个数:" + list.size());
        System.out.println(list.toString());
        // 2.删除元素
        /*
        list.remove("二");
        list.remove(0); // 下标方式删除
        System.out.println("删除后:"+list.size());
        System.out.println(list.toString());
        */
        // 3.遍历元素
        // 3.1 for 遍历
        System.out.println("-----------3.1 for 遍历------------");
        for (int i = 0; i < list.size(); i++) {
            // get() 获取集合中的元素,i 为下标
            System.out.println(i + ":" + list.get(i));
        }
        // 3.2 增强 for 遍历
        System.out.println("-----------3.2 增强 for 遍历------------");
        for (Object o : list) {
            System.out.println(o);
        }
        // 3.3 迭代器遍历 Iterator
        System.out.println("-----------3.3 Iterator 迭代器遍历------------");
        Iterator it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 3.4 列表迭代器遍历:ListIterator 可以双向遍历,添加、删除及修改元素
        System.out.println("-----------3.4 ListIterator 迭代器遍历 从前向后------------");
        ListIterator lit = list.listIterator();
        while (lit.hasNext()) {
            System.out.println(lit.nextIndex() + ":" + lit.next());
        }
        // 3.5 列表迭代器,反向遍历(迭代时,“遍历指针” 需指向末尾)
        System.out.println("-----------3.5 列表迭代器 从后向前------------");
        while (lit.hasPrevious()) {
            System.out.println(lit.previousIndex() + ":" + lit.previous());
        }
        // 4.判断
        System.out.println(list.contains("二")); // 是否包含元素
        System.out.println(list.isEmpty()); // 是否为空
        // 5.获取位置
        System.out.println(list.indexOf("一"));
    }
}
package com.base.demo09;

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

/*
 * List 使用(二)
 * */
public class Demo04 {
    public static void main(String[] args) {
        // 创建对象
        List list = new ArrayList();
        // 1.添加数字数据(自动装箱)
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);
        list.add(60);
        System.out.println("元素个数:" + list.size());
        System.out.println(list.toString());
        // 2.删除:remove()内通过下标删除,需要将删除的数字做类型转换
        // list.remove(0);
        // list.remove(20);  异常,数组越界
        list.remove(new Integer(20));   // 或 list.remove((Object) 20);
        System.out.println("删除后:" + list.size());
        System.out.println(list.toString());

        // 3.subList() 返回子集合(含头不含尾)
        List list2 = list.subList(0, 3);
        System.out.println(list2.toString());
    }
}

五、List 实现类

  1. ArrayList【重点】
package com.base.demo09;

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

/**
 * ArrayList的使用
 * 存储结构:数组;
 * 特点:查找遍历速度快,增删慢。
 * 1.添加元素
 * 2.删除元素
 * 3.遍历元素(重点)
 * 4.判断
 * 5.查找
 */
public class Demo05 {
    public static void main(String[] args) {
        // 创建对象  (size 0, 容量 0,每次扩容大小是原来的 1.5 倍)
        ArrayList arrayList = new ArrayList<>();
        // 创建学生类对象
        Student s1 = new Student("学生1", 20);
        Student s2 = new Student("学生2", 21);
        Student s3 = new Student("学生3", 19);
        // 1.添加元素
        arrayList.add(s1);
        arrayList.add(s2);
        arrayList.add(s3);
        System.out.println("元素个数:" + arrayList.size());
        System.out.println(arrayList.toString());
        // 2.删除元素
//        arrayList.remove(s1);
        // 和 s1,是两个不同的对象,需重写 equals(this==obj) 方法
        arrayList.remove(new Student("学生1", 20)); 
        System.out.println("删除之后:" + arrayList.size());

        // 3.遍历元素(重点): for、增强for、迭代器、列表迭代器
        // 3.1迭代器 Iterator
        System.out.println("---------------3.1迭代器 Iterator------------");
        Iterator it = arrayList.iterator();
        while (it.hasNext()) {
            Student s = (Student) it.next();    // 类型转换:对象类转换为 Student 类
            System.out.println(s.toString());
        }
        // 3.2列表迭代器 ListIterator
        System.out.println("---------------3.2列表迭代器 ListIterator------------");
        ListIterator lit = arrayList.listIterator();
        while (lit.hasNext()) {
            Student s = (Student) lit.next();
            System.out.println(s.toString());
        }
        // 3.3列表迭代器(逆序:从后往前遍历)
        System.out.println("---------------3.3列表迭代器(逆序)------------");
        while (lit.hasPrevious()){
            Student s = (Student) lit.previous();
            System.out.println(s.toString());
        }
        // 4.判断
        // 是否包含对象
        System.out.println(arrayList.contains(s2)); // true
        // 重写 equals() 方法后
        System.out.println(arrayList.contains(new Student("学生2", 21))); // true
        System.out.println(arrayList.isEmpty());
        // 5.查找
        System.out.println(arrayList.indexOf(s3));
        System.out.println(arrayList.indexOf(new Student("学生2", 21)));
    }
}

注:Object 里的 equals(this==obj) 用地址和当前对象比较,如果想实现代码中的问题,可以在学生类中重写 equals 方法:查看 equals 方法重写

// 重写 equals() 方法
    @Override
    public boolean equals(Object obj) {
        // 1.判断是否是同一对象
        if (this == obj) {
            return true;
        }
        // 2.判断是否为空
        if (obj == null) {
            return false;
        }
        // 3.判断是否是 Student 类型
        if (obj instanceof Student) {
            Student s = (Student) obj;    // 类型转换
            // 4.比较属性
            if (this.name.equals(s.getName()) && this.age == s.getAge()) {
                return true;
            }
        }
        // 5.不满足条件,返回false
        return false;
    }

//    IDEA 方法1
//    @Override  
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (!(o instanceof Student)) return false;
//
//        Student student = (Student) o;
//
//        if (getAge() != student.getAge()) return false;
//        return getName().equals(student.getName());
//    }

//    IDEA 方法2
//    @Override
//    public boolean equals(Object o) {
//        if (this == o) return true;
//        if (!(o instanceof Student)) return false;
//
//        Student student = (Student) o;
//
//        if (age != student.age) return false;
//        return name.equals(student.name);
//    }

ArrayList 源码分析

//这是一个空的数组
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 无参构造
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

注:没有向集合中添加任何元素时,集合容量为 0。(默认的 10 个容量怎么来的?)查看 add 方法源码:

// add 方法源码
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;
    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
  1. Vector(使用不多)
package com.base.demo10;

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

/*
 * Vector 集合的使用
 * 存储结构:数组
 * */
public class Demo01 {
    public static void main(String[] args) {
        // 创建对象
        Vector vector = new Vector<>();
        // 1.添加元素
        vector.add("vector01");
        vector.add("vector02");
        vector.add("vector03");
        System.out.println("元素个数:" + vector.size());
        System.out.println(vector.toString());
        // 2.删除
        // vector.remove(0);
        // vector.remove("vector02");
        // vector.clear(); // 清空
        // System.out.println("删除后个数:"+vector.size());
        // 3.遍历 (for、增强for、枚举器)
        // 枚举器遍历
        Enumeration en = vector.elements();
        while (en.hasMoreElements()) {
            String o = (String) en.nextElement();
            System.out.println(o);
        }
        // 4.判断
        System.out.println(vector.contains("vector01"));
        System.out.println(vector.isEmpty());
        // 5.vector 其它方法
        System.out.println(vector.firstElement());  // 获取第一个元素
        System.out.println(vector.lastElement());   // 获取最后一个元素
        System.out.println(vector.elementAt(0));    // 按下标获取元素
        System.out.println(vector.get(0));  // 按下标获取元素
    }
}
  1. LinkedList(双向链表)
package com.base.demo10;

import com.base.demo09.Student;

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

/*
 * LinkedList 的使用
 * 存储结构:双向链表
 * */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        LinkedList linkedList = new LinkedList<>();
        // 1.添加元素
        // 创建学生类对象
        Student s1 = new Student("学生1", 20);
        Student s2 = new Student("学生2", 21);
        Student s3 = new Student("学生3", 19);
        linkedList.add(s1);
        linkedList.add(s2);
        linkedList.add(s3);
        System.out.println("元素个数:" + linkedList.size());
        System.out.println(linkedList.toString());
        // 2.删除元素
        // linkedList.remove(s1);
        // linkedList.remove(new Student("学生2", 21));  // 需重写equals()方法
        // System.out.println("删除后:" + linkedList.size());
        // 3.遍历
        // 3.1 for遍历
        System.out.println("--------------3.1 for遍历---------------");
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.println(linkedList.get(i));
        }
        // 3.2 增强 for 遍历
        System.out.println("--------------3.2 增强 for 遍历---------------");
        for (Object o : linkedList) {
            Student s = (Student) o;    // 类型转换
            System.out.println(s.toString());
        }
        // 3.3 迭代器遍历
        System.out.println("--------------3.3 迭代器遍历---------------");
        Iterator it = linkedList.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 3.4 列表迭代器遍历
        System.out.println("--------------3.4 列表迭代器遍历---------------");
        ListIterator lit = linkedList.listIterator();
        while (lit.hasNext()) {
            System.out.println(lit.next());
        }
        // 3.5 列表迭代器遍历(反向) 需集合指针指向末尾
        System.out.println("--------------3.5 列表迭代器遍历(反向)-------------");
        while (lit.hasPrevious()) {
            System.out.println(lit.previous());
        }
        // 4.判断
        System.out.println(linkedList.contains(s1));
        System.out.println(linkedList.isEmpty());
        // 5.获取位置
        System.out.println(linkedList.indexOf(s3));
    }
}

LinkedList 源码分析

private static class Node<E> {
        E item;
        Node<E> next;
        Node<E> prev;

        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
    }
public boolean add(E e) {
    linkLast(e);
    return true;
}
void linkLast(E e) {
        final Node<E> l = last;
        final Node<E> newNode = new Node<>(l, e, null);
        last = newNode;
        if (l == null)
            first = newNode;
        else
            l.next = newNode;
        size++;
        modCount++;
    }

ArrayList 和 LinkedList 区别

六、泛型概述

  1. 泛型类
/**
 * 泛型类
 * 语法:类名<T>
 * T 是类型占位符,表示一种引用类型,编写多个使用逗号隔开 <T,E,K...>
 */
public class MyGeneric<T>{
    // 1.创建泛型变量
    //不能使用 new 来创建,因为泛型是不确定的类型,也可能拥有私密的构造方法。
    T t;
    // 2.泛型作为方法的参数
    public void show(T t) {
        System.out.println(t);
    }
    // 3.泛型作为方法的返回值
    public T getT() {
        return t;
    }
}
package com.base.demo10;

/**
 * 注意:
 * 1.泛型只能使用引用类型
 * 2.不同泛型类型的对象,不能相互赋值
 */
public class TestGeneric {
    public static void main(String[] args) {
        // 使用泛型类创建对象
//        MyGeneric<String> myGeneric = new MyGeneric<String>();    后面的类型可省略
        MyGeneric<String> myGeneric = new MyGeneric<>();
        myGeneric.t = "Hello";
        myGeneric.show("hello");
        String s = myGeneric.getT();
        System.out.println(s);

        MyGeneric<Integer> myGeneric1 = new MyGeneric<>();
        myGeneric1.t = 100;
        myGeneric1.show(200);
        Integer integer = myGeneric1.getT();
        System.out.println(integer);
    }
}
  1. 泛型接口
package com.base.demo10;

/*
 * 泛型接口
 * 语法:接口名<T>
 * 注意:不能创建泛型静态常量
 * */
public interface MyInterface<T> {
    String name = "Liu";

    T server(T t);
}
package com.base.demo10;

/**
 * 实现接口时,确定泛型类
 */
public class MyInterfaceImpl implements MyInterface<String> {
    @Override
    public String server(String t) {
        System.out.println(t);
        return t;
    }
}
//测试
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.server("泛型接口测试");  // 泛型接口测试
package com.base.demo10;

/**
 * 实现接口时,不确定泛型类
 */
public class MyInterfaceImpl2<T> implements MyInterface<T> {

    @Override
    public T server(T t) {
        System.out.println(t);
        return t;
    }
}
//测试
MyInterfaceImpl2<Integer> impl2 = new MyInterfaceImpl2<>();
impl2.server(2000);  // 2000
  1. 泛型方法
package com.base.demo10;

/**
 * 泛型方法
 * 语法:<T> 返回类型
 */
public class MyGenericMethod {
    // 泛型方法
    public <T> T show(T t) {
        System.out.println("泛型方法" + t);
        return t;
    }
}
//测试类:泛型方法
MyGenericMethod myGenericMethod = new MyGenericMethod();
myGenericMethod.show("字符串");    // String
myGenericMethod.show(200);  // Integer
myGenericMethod.show(3.14); // Double
  1. 泛型集合
public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable {
    // ......
}
package com.base.demo10;

import com.base.demo09.Student;

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

public class Demo03 {
    public static void main(String[] args) {
//        ArrayList<String> arrayList = new ArrayList<String>();    后面的类型可不写
        ArrayList<String> arrayList = new ArrayList<>();
        arrayList.add("字符串1");
        arrayList.add("字符串2");
//        arrayList.add(100);   报错:不能添加不同类型
//        arrayList.add(3.14);
        for (String s : arrayList) {    // 直接为 String 类型,不需类型转换
            System.out.println(s);
        }

        ArrayList<Student> arrayList1 = new ArrayList<>();
        Student s1 = new Student("学生1", 20);
        Student s2 = new Student("学生2", 21);
        Student s3 = new Student("学生3", 19);
        arrayList1.add(s1);
        arrayList1.add(s2);
        arrayList1.add(s3);
//        arrayList1.add(100); 报错:不能添加 Student 之外的类型
        Iterator<Student> it = arrayList1.iterator();
        while (it.hasNext()) {
            Student s = it.next();  // 无需强制转换
            System.out.println(s.toString());
        }
//        arrayList = arrayList1;   报错:不同类型之间的泛型,不能相互赋值
    }
}

七、Set 集合概述

Set 子接口

package com.base.demo11;

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

/**
 * 测试Set接口的使用
 * 特点:1.无序,没有下标;2.重复
 * 1.添加数据(无序)
 * 2.删除数据
 * 3.遍历【重点】
 * 4.判断
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建集合
        HashSet<String> set = new HashSet<>();
        // 1.添加数据(无序)
        set.add("字符串3");
        set.add("字符串1");
        set.add("字符串2");
//        set.add("字符串2");  不能重复
        System.out.println("数据个数:" + set.size());
        System.out.println(set.toString());
        // 2.删除数据
//        set.remove("字符串1"); // 无下标,不能通过下标删除
//        System.out.println(set.toString());
        // 3.遍历 (两种:增强 for、迭代器)
        // 3.1增强 for 遍历
        System.out.println("------------3.1增强 for 遍历----------");
        for (String s : set) {
            System.out.println(s);
        }
        // 3.2迭代器遍历
        System.out.println("------------3.2迭代器遍历----------");
        Iterator<String> it = set.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 4.判断
        System.out.println(set.contains("字符串1"));
        System.out.println(set.isEmpty());
    }
}

八、Set 实现类

  1. HashSet【重点】
package com.base.demo11;

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

/**
 * HashSet集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 * 1.添加元素(无序)
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        // JDK 1.8 后,后面的泛型类型,可以不用写
        HashSet<String> hashSet = new HashSet<>();  
        // 1.添加元素
        hashSet.add("字符串1");
        hashSet.add("字符串2");
        hashSet.add("字符串3");
        hashSet.add("字符串4");
//        hashSet.add("字符串4"); 不能添加重复元素
        System.out.println("元素个数:" + hashSet.size());
        System.out.println(hashSet.toString()); // 无序
        // 2.删除元素
//        hashSet.remove("字符串2");
//        System.out.println("删除后:"+hashSet.size());
        // 3.遍历(两种:增强for、迭代器)
        // 3.1增强 for
        System.out.println("-----------3.1增强 for----------");
        for (String s : hashSet) {
            System.out.println(s);
        }
        // 3.2迭代器
        System.out.println("-----------3.2迭代器----------");
        Iterator<String> it = hashSet.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 4.判断
        System.out.println(hashSet.contains("字符串3"));
        System.out.println(hashSet.isEmpty());
    }
}
package com.base.demo11;

// 创建 Person 类
public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(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 "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package com.base.demo11;

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

/**
 * HashSet 集合的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 * 1.添加元素(无序)
 * 2.删除元素
 * 3.遍历
 * 4.判断
 */
public class Demo03 {
    public static void main(String[] args) {
        // 创建集合
        HashSet<Person> persons = new HashSet<>();
        // 创建 Person 对象
        Person p1 = new Person("张三", 25);
        Person p2 = new Person("李四", 23);
        Person p3 = new Person("王五", 26);
        // 1.添加元素
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
//        person.add(p3); 重复,不能添加
        // 不同对象,相同属性值,需要重写 hashCode 和 equals 方法
        // 来实现(否则不视为重复,可以添加)
//        person.add(new Person("王五", 26));
        System.out.println("元素个数:" + persons.size());
        System.out.println(persons.toString());
        // 2.删除元素
//        persons.remove(p1);
        // 未重写 hashCode 和 equals 方法时,不能删除
//        person.remove(new Person("张三", 25));

        System.out.println("删除后:" + persons.size());
        // 3.遍历
        // 3.1增强for
        System.out.println("----------3.1增强for-----------");
        for (Person person : persons) {
            System.out.println(person.toString());
        }
        // 3.2迭代器
        System.out.println("----------3.2迭代器-----------");
        Iterator<Person> it = persons.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 4.判断
        System.out.println(persons.contains(p1));
        // 与 p1 属性值相同,未重写 hashCode 和 equals 方法时,结果为 false
        System.out.println(persons.contains(new Person("张三", 25)));
        System.out.println(persons.isEmpty());
    }
}

注:HashSet 存储过程:(重复依据)

// IDEA 快捷生成 Alt + Insert
@Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        return name != null ? name.equals(person.name) : person.name == null;
    }

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

HashSet 补充:hashCode 方法里,使用数字 31 的原因:

  1. 31 是一个质数(只能被 1 和它自身整除),这样的数字在计算时,可以尽量减少散列冲突(hashCode 值尽量不一样)。
  2. 可以提高执行效率,31 * i = ( i << 5 ) - i。31 乘以一个数,可以转换成移位操作,这样计算更快。

2.TreeSet

package com.base.demo11;

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

/**
 * TreeSet 使用
 * 存储结构:红黑树
 */
public class Demo04 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<String> treeSet = new TreeSet<>();
        // 1.添加元素(自动排序)
        treeSet.add("xyz");
        treeSet.add("abc");
        treeSet.add("hello");
//        treeSet.add("xyz"); 不能重复
        System.out.println("元素个数:" + treeSet.size());
        System.out.println(treeSet.toString()); // 结果自动排序
        // 2.删除元素
//        treeSet.remove("hello");
//        System.out.println("删除后:" + treeSet.size());
        // 3.遍历(增强for、迭代器)
        // 3.1增强for
        System.out.println("-----------3.1增强for----------");
        for (String s : treeSet) {
            System.out.println(s);
        }
        // 3.2迭代器
        System.out.println("-----------3.2迭代器----------");
        Iterator<String> it = treeSet.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
        // 4.判断
        System.out.println(treeSet.contains("xyz"));
        System.out.println(treeSet.isEmpty());
    }
}
package com.base.demo11;

import java.util.TreeSet;

/**
 * 使用 TreeSet 保存数据
 * 存储结构:红黑树
 * 要求:元素类必须实现Comparable接口,compareTo方法返回 0,认为是重复元素
 */
public class Demo05 {
    public static void main(String[] args) {
        // 创建集合
        TreeSet<Person> persons = new TreeSet<>();
        // 创建 Person 对象
        Person p1 = new Person("张三", 25);
        Person p2 = new Person("李四", 23);
        Person p3 = new Person("王五", 26);
        Person p4 = new Person("王五", 23);
//        Person p5 = new Person("王五", 23);   相同属性值 不能添加
        // 1.添加元素(元素类必须实现 Comparable 接口,及内部抽象方法)
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        // 位置在 p3 之前:通过重写 compareTo 方法排序
        persons.add(p4);
        System.out.println("元素个数:" + persons.size());
        System.out.println(persons.toString());
        // 2.删除元素
        persons.remove(p1);
        // 可以删除:重写 compareTo 方法后,比较的是属性值是否相同
        persons.remove(new Person("李四", 23));
        System.out.println("删除后:" + persons.size());
        // 3.遍历(略)
        // 4.判断
        System.out.println(persons.contains(p3));
        // true:重写 compareTo 方法后,比较的是属性值是否相同
        System.out.println(persons.contains(new Person("王五", 26)));
    }
}
public class Person implements Comparable<Person> {
    // 内部代码 略......
    @Override
    public int compareTo(Person o) {
        // 先比较 name(相同为 0)
        int n1 = this.name.compareTo(o.getName());
        // 再比较 age
        int n2 = this.age - o.getAge();
        // name 相同的话,返回 n2,否则返回 n1
        return n1 == 0 ? n2 : n1;
    }
}

Comparator:实现定制比较(比较器)

package com.base.demo11;

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

/**
 * TreeSet的使用
 * Comparator:实现定制比较(比较器)
 */
public class Demo06 {
    public static void main(String[] args) {
        // 创建集合,并通过匿名内部类,指定比较规则(Comparator)
        TreeSet<Person> persons = new TreeSet<>(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                // 先比较 age
                int n1 = o1.getAge() - o2.getAge();
                // 再比较 name
                int n2 = o1.getName().compareTo(o2.getName());
                return n1 == 0 ? n2 : n1;
            }
        });
        // 创建 Person 对象
        Person p1 = new Person("张三", 25);
        Person p2 = new Person("李四", 23);
        Person p3 = new Person("王五", 26);
        Person p4 = new Person("王五", 23);
        // 添加元素(元素类必须实现 Comparable 接口,及内部抽象方法)
        persons.add(p1);
        persons.add(p2);
        persons.add(p3);
        persons.add(p4);
        System.out.println("元素个数:" + persons.size());
        // 结果为按 age 排序
        System.out.println(persons.toString());
    }
}
package com.base.demo11;

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

/**
 * 要求:使用 TreeSet集合实现字符串按照长度进行排序
 * helloworld zhangsan lisi wangwu beijing xian nanjing
 * Comparator 接口实现定制比较
 */
public class Demo07 {
    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("helloworld");
        treeSet.add("zhangsan");
        treeSet.add("lisi");
        treeSet.add("wangwu");
        treeSet.add("beijing");
        treeSet.add("xian");
        treeSet.add("nanjing");
        System.out.println(treeSet.toString());
        // [lisi, xian, wangwu, beijing, nanjing, zhangsan, helloworld]
    }
}

九、Map 体系集合

Map 集合概述

类型 方法名 说明
V put(K key,V value) 将对象存入到集合中,关联键值。key 重复则覆盖原值。
V get(Object key) 根据键获取相应的值。
Set<K> keySet() 返回所有的 key
Collection<V> values() 返回包含所有值的 Collection 集合。
Set<Map.Entry<K,V>> entrySet() 键值匹配的 set 集合
package com.base.demo12;

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

/**
 * Map接口的使用
 * 特点:1.存储键值对 2.键不能重复,值可以重复 3.无序
 */
public class Demo01 {
    public static void main(String[] args) {
        // 创建 Map 集合
        HashMap<String, String> map = new HashMap<>();
        // 1.添加元素(无序)
        map.put("cn", "中国");
        map.put("uk", "英国");
        map.put("usa", "美国");
//        map.put("cn", "China"); 相同键名,可以添加,但个数不增加,值会替换 China
        System.out.println("元素个数:" + map.size());
        System.out.println(map.toString()); // {usa=美国, uk=英国, cn=中国}
        // 2.删除元素
//        map.remove("usa");
//        System.out.println("删除后:" + map.size());
        // 3.遍历:(1.keySet() 2.entrySet() 效率高)
        // 3.1 keySet(): 返回所有的 key(Set 集合)
        System.out.println("------------3.1 keySet()-----------");
//        Set<String> keySet = map.keySet();
//        for (String key : keySet) {
        // 简化
        for (String key : map.keySet()) {
            // map.get(key):通过键获取值
            System.out.println(key + ":" + map.get(key));
        }
        // 3.2 entrySet():返回 Set 集合(Map.Entry)的键值对 K-V
        System.out.println("------------3.2 entrySet()-----------");
//        Set<Map.Entry<String, String>> entries = map.entrySet();
//        for (Map.Entry<String, String> entry : entries) {
        // 简化
        for (Map.Entry<String, String> entry : map.entrySet()) {
            // getKey():获取键     getValue():获取值
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        // 4.判断
        System.out.println(map.containsKey("cn"));
        System.out.println(map.containsValue("中国"));
    }
}

十、Map 集合的实现类

1. HashMap【重点】

package com.base.demo12;

// 学生类
public class Student {
    private String name;
    private int stuNo;

    public Student() {
    }

    public Student(String name, int stuNo) {
        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 +
                '}';
    }
}
package com.base.demo12;

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

/**
 * HashMap 的使用
 * 存储结构:哈希表(数组+链表+红黑树)
 */
public class Demo02 {
    public static void main(String[] args) {
        // 创建集合
        HashMap<Student, String> students = new HashMap<>();
        // 创建 Student 对象
        Student s1 = new Student("学生1", 100);
        Student s2 = new Student("学生2", 101);
        Student s3 = new Student("学生3", 102);
        // 1.添加元素(无序)
        students.put(s1, "北京");
        students.put(s2, "上海");
        students.put(s3, "杭州");
        // 添加失败,但会更新值
//        students.put(s3, "南京");
        // 添加成功:与 s3 属性值相同,但不是同一对象
        // 使用 key 的 hashCode 和 equals 作为重复依据(去重,需重写两个方法)
//        students.put(new Student("学生3", 102), "杭州");   重写两方法后,无法添加
        System.out.println(students.size());
        System.out.println(students.toString());
        // 2.删除元素
//        students.remove(s1);
//        System.out.println("删除后:"+students.size());
        // 3.遍历(两种:keySet、entrySet)
        // 3.1 keySet() 遍历 (获取 key)
        System.out.println("-----------3.1 keySet() 遍历-----------");
        for (Student key : students.keySet()) {
            System.out.println(key.toString() + ":" + students.get(key));
        }
        // 3.2 entrySet() 遍历 (获取 K-V 键值对)
        System.out.println("-----------3.2 entrySet() 遍历-----------");
        for (Map.Entry<Student, String> entry : students.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        // 4.判断
        System.out.println(students.containsKey(s1));
        // 重写方法后,结果为 true
        System.out.println(students.containsKey(new Student("学生1", 100)));
        System.out.println(students.containsValue("北京"));
    }
}
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Student)) return false;

        Student student = (Student) o;

        if (stuNo != student.stuNo) return false;
        return Objects.equals(name, student.name);
    }

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

HashMap 源码分析

  1. 默认初始化容量:static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
  2. 数组最大容量:static final int MAXIMUM_CAPACITY = 1 << 30;
  3. 默认加载因子:static final float DEFAULT_LOAD_FACTOR = 0.75f;
  4. 链表调整为红黑树的链表长度阈值(JDK 1.8):static final int TREEIFY_THRESHOLD = 8;
  5. 红黑树调整为链表的链表长度阈值(JDK 1.8):static final int UNTREEIFY_THRESHOLD = 6;
  6. 链表调整为红黑树的数组最小阈值(JDK 1.8):static final int MIN_TREEIFY_CAPACITY = 64;
  7. HashMap 存储的数组:transient Node<K,V>[] table;
  8. HashMap 存储的元素个数:transient int size;

默认加载因子

链表调整为红黑树的链表长度阈值

红黑树调整为链表的链表长度阈值

HashMap 的数组 table

static class Node<K,V> implements Map.Entry<K,V> {
      final K key;
      V value;
      Node<K,V> next;
  }
public HashMap() {
      this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
  }
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{
          //略
      }
  }
final Node<K,V>[] resize() {
      Node<K,V>[] oldTab = table;
      int oldCap = (oldTab == null) ? 0 : oldTab.length;
      int oldThr = threshold;
      if (oldCap > 0);
      else if (oldThr > 0);
      else {               // zero initial threshold signifies using defaults
          newCap = DEFAULT_INITIAL_CAPACITY;
          newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
      } 
      @SuppressWarnings({"rawtypes","unchecked"})
      Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
      table = newTab;
      return newTab;
  }
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict){
      if (++size > threshold)
          resize();
  }
final Node<K,V>[] resize() {
      int oldCap = (oldTab == null) ? 0 : oldTab.length;
      int newCap;
      if (oldCap > 0) {
          if (oldCap >= MAXIMUM_CAPACITY) {//略}
          else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                   oldCap >= DEFAULT_INITIAL_CAPACITY)
      }
  }

HashMap 源码总结

  1. HashMap 刚创建时,table=null,目的是节省空间,当添加第一个元素时,table 容量调整为 16;
  2. 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的 2 倍。目的是减少调整元素的个数;
  3. JDK 1.8:当每个链表长度大于 8,且数组元素个数大于等于 64 时,会调整为红黑树,目的是提高执行效率;
  4. JDK 1.8:当链表长度小于 6 时,调整成链表;
  5. JDK 1.8 以前,链表是头插入,JDK 1.8 以后是尾插入。

HashSet 源码分析

public class HashSet<E>
      extends AbstractSet<E>
      implements Set<E>, Cloneable, java.io.Serializable
  {
      private transient HashMap<E,Object> map;
      private static final Object PRESENT = new Object();
      public HashSet() {
          map = new HashMap<>();
      }
  }
public boolean add(E e) {
      return map.put(e, PRESENT)==null;
  }
  1. HashTable(了解即可,基本不用)
  1. Properties:配置文件的读取(和流相关)查看 I/O流

4.TreeMap

package com.base.demo12;

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

/**
 * TreeMap 的使用
 * 存储结构:红黑树
 */
public class Demo03 {
    public static void main(String[] args) {
        // 创建集合
//        TreeMap<Student, String> treeMap = new TreeMap<>();
        TreeMap<Student, String> treeMap = new TreeMap<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                int n1 = o1.compareTo(o2);
                return n1;
            }
        });
        // 创建 Student 对象
        Student s1 = new Student("学生1", 100);
        Student s2 = new Student("学生2", 101);
        Student s3 = new Student("学生3", 102);
        // 1.添加元素
        treeMap.put(s1, "北京");
        treeMap.put(s2, "上海");
        treeMap.put(s3, "杭州");
        // 添加失败,但会更新值
//        treeMap.put(new Student("学生3", 102), "南京");
        // 不能直接打印,需要实现 Comparable 接口,因为红黑树需要比较大小
        System.out.println("元素个数:" + treeMap.size());
        System.out.println(treeMap.toString());
        // 2.删除元素
//        treeMap.remove(s1);
        // 可删除,需实现 Comparable 接口
//        treeMap.remove(new Student("学生3", 102));
//        System.out.println("删除后:"+treeMap.size());
        // 3.遍历(两种:keySet、entrySet)
        // 3.1 keySet() 遍历(获取 Key)
        System.out.println("-----------3.1 keySet() 遍历-----------");
        for (Student key : treeMap.keySet()) {
            System.out.println(key + ":" + treeMap.get(key));
        }
        // 3.2 entrySet() 遍历(获取 K-V 键值对)
        System.out.println("-----------3.2 entrySet() 遍历-----------");
        for (Map.Entry<Student, String> entry : treeMap.entrySet()) {
            System.out.println(entry.getKey() + ":" + entry.getValue());
        }
        // 4.判断
        System.out.println(treeMap.containsKey(s1));
        System.out.println(treeMap.containsKey(new Student("学生1", 100)));
        System.out.println(treeMap.containsValue("杭州"));
    }
}
public class Student implements Comparable<Student>{
    @Override
    public int compareTo(Student o) {
        int n1 = this.stuNo - o.stuNo;
        return n1;
    }
}
TreeMap<Student, String> treeMap = new TreeMap<>(new Comparator<Student>() {
    @Override
    public int compare(Student o1, Student o2) {
        int n1 = o1.compareTo(o2);
        return n1;
    }
});

TreeSet 源码

public class TreeSet<E> extends AbstractSet<E>
    implements NavigableSet<E>, Cloneable, java.io.Serializable
{
    private transient NavigableMap<E,Object> m;
    private static final Object PRESENT = new Object();
    TreeSet(NavigableMap<E,Object> m) {
        this.m = m;
    }
    public TreeSet() {
        this(new TreeMap<E,Object>());
    }
}
public boolean add(E e) {
    return m.put(e, PRESENT)==null;
}

十一、Collections 工具类

方法名 说明
public static void reverse(List<?> list) 反转集合中元素的顺序
public static void shuffle(List<?> list) 随机重置集合元素的顺序
public static void sort(List<T> list) 升序排序(元素类型必须实现 Comparable 接口)
package com.base.demo12;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Collections 工具类的使用
 */
public class Demo04 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(20);
        list.add(5);
        list.add(12);
        list.add(30);
        list.add(6);
        // sort 排序
        System.out.println("---------排序前--------");
        System.out.println(list.toString());
        System.out.println("---------排序后--------");
        Collections.sort(list);
        System.out.println(list.toString());
        // binarySearch 二分查找(查找位置)
        int i = Collections.binarySearch(list, 12);
        System.out.println(i);
        // copy 复制
        ArrayList<Integer> list1 = new ArrayList<>();
        // 将新的集合长度等于源集合长度
        for (int i1 = 0; i1 < list.size(); i1++) {
            list1.add(0);
        }
        // 该方法要求目标元素容量,大于等于源目标
        // 后者 list 为复制的集合
        Collections.copy(list1, list);
        System.out.println("复制:" + list1.toString());
        // reverse 反转
        Collections.reverse(list);
        System.out.println("反转后:" + list);
        // shuffle 打乱顺序
        Collections.shuffle(list);
        System.out.println("打乱顺序后:" + list);

        // 补充:1.list 转数组
        System.out.println("------------补充:1.list 转数组-----------");
        Integer[] arr = list.toArray(new Integer[0]);
        System.out.println(arr.length);
        System.out.println(Arrays.toString(arr));

        // 补充:2.数组转集合
        System.out.println("------------补充:2.数组转集合-----------");
        String[] names = {"张三", "李四", "王五"};
        // 数组转集合:受限集合,不能添加和删除
        List<String> list2 = Arrays.asList(names);
//        list2.add("aa");  错误,不能添加
//        list2.remove("张三");   错误,不能删除
        System.out.println(list2);
        // 注:基本类型转成集合时,需要修改为包装类
        Integer[] nums = {100, 200, 300, 400, 500};
        List<Integer> list3 = Arrays.asList(nums);
        System.out.println(list3);
    }
}

总结:

上一篇下一篇

猜你喜欢

热点阅读