【JavaSE学习笔记】【完结】chapter07-集合

第七章 集合

JavaSE学习笔记-chapter07-集合【完结】

学习资源:动力节点java学习

更新日志

  • 2024.6.6更新至13小节(手写单向链表)
  • 2024.7.16更新至21小节(Properties)
  • 2024.7.18更新至26小结(Collections工具类)

完整笔记

笔记1更新至单向链表
笔记2更新至Properties类
笔记3完结更新至Collections工具类

1. 集合概述

2. 集合的继承结构(Java21)

3. Collection接口的通用方法

package addery.zeus.com.collectiontest;

import org.junit.jupiter.api.Test;

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

/**
 *  Collection接口的通用方法
 */
public class CollectionMethodTest {

    // 向集合中添加元素 获取集合中元素个数
    @Test
    public void testAddAndSize() {
        Collection c = new ArrayList();

        c.add(10); // 自动装箱
        c.add(3.14); // 自动装箱
        c.add(false); // 自动装箱
        c.add("Addery");
        c.add(new Object());

        System.out.println(c.size()); // 5
    }

    // 将参数集合中所有元素全部加入当前集合
    @Test
    public void testAddAll() {
        Collection c1 = new ArrayList();
        c1.add(10);
        c1.add(3.14);
        c1.add(false);

        Collection c2 = new ArrayList();
        c2.addAll(c1);
        System.out.println(c2.size()); // 3
    }

    // 判断集合中是否包含对象o
    @Test
    public void testContains() {
        Collection c1 = new ArrayList();
        c1.add(10);
        c1.add(3.14);
        c1.add(false);
        c1.add("addery");

        System.out.println(c1.contains(10)); // true
        System.out.println(c1.contains(100)); // false

        String s = new String("addery");

        // 重写了equals方法,比较时使用equals
        System.out.println(c1.contains(s)); // true
    }

    // 从集合中删除对象o
    @Test
    public void testRemove() {
        Collection c1 = new ArrayList();
        c1.add(10);
        c1.add(3.14);
        c1.add(false);
        c1.add("addery");

        System.out.println(c1.size()); // 4
        System.out.println(c1.remove(10)); // true
        System.out.println(c1.size()); // 3

        System.out.println(c1.remove(10)); // false

        String s = new String("addery");
        // 底层会调用equals方法来完成删除
        System.out.println(c1.remove(s)); // true
        System.out.println(c1.size()); // 2
    }

    // 清空集合 判断集合元素个数是否为空
    @Test
    public void testClear() {
        Collection c1 = new ArrayList();
        c1.add(10);
        c1.add(3.14);
        c1.add(false);
        c1.add("addery");

        System.out.println(c1.size()); // 4
        System.out.println(c1.isEmpty()); // false
        c1.clear();
        System.out.println(c1.size()); // 0
        System.out.println(c1.isEmpty()); // true
    }

    // 将集合转换成一维数组
    @Test
    public void testToArray() {
        Collection c1 = new ArrayList();
        c1.add(10);
        c1.add(3.14);
        c1.add(false);
        c1.add(new Object());

        Object[] array = c1.toArray();
.
        for(Object obj : array) {
            System.out.println(obj);
        }
    }

}

4. Collection的通用遍历方式

package addery.zeus.com.collectiontest;

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

/**
 * Collection接口通用迭代器
 */
public class CollectionTest01 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection c1 = new ArrayList();

        // 向集合中添加元素
        c1.add(10);
        c1.add(3.14);
        c1.add(true);

        // 创建迭代器对象
        Iterator iterator = c1.iterator();
        System.out.println(iterator);

        // 判断当前光标处是否有元素
        while(iterator.hasNext()) {
            // 返回当前元素,并将光标向后移动一格
            Object next = iterator.next();
            System.out.println(next);
        }
    }
}

5. SequencedCollection接口

package addery.zeus.com.sequncedcollectiontest;

import org.junit.jupiter.api.Test;

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

/**
 * SequencedCollection接口常用方法单元测试
 * ArrayList LinkedList Vector LinkedHashSet TreeSet Stack都可以调用这个接口中的方法
 */
public class SequencedCollectionMethodTest {
    /**
     * 向头部添加元素 向末尾添加
     */
    @Test
    public void testAddFirstAndAddLast() {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("zhangsan1");
        sc.add("zhangsan2");
        sc.add("zhangsan3");
        sc.add("zhangsan4");
        sc.add("zhangsan5");
        sc.add("zhangsan6");
        sc.addFirst("Zeus");
        sc.addLast("Addery");

        Iterator<String> iterator = sc.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }

    /**
     * 删除头部 删除末尾
     */
    @Test
    public void testRemoveFirstAndRemoveLast() {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("zhangsan1");
        sc.add("zhangsan2");
        sc.add("zhangsan3");
        sc.add("zhangsan4");
        sc.add("zhangsan5");
        sc.add("zhangsan6");

        sc.removeFirst();
        sc.removeLast();

        Iterator<String> it = sc.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }
    }

    /**
     * 获取头部节点  获取尾部节点
     */
    @Test
    public void testGetFirstAndGetLast() {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("zhangsan1");
        sc.add("zhangsan2");
        sc.add("zhangsan3");
        sc.add("zhangsan4");
        sc.add("zhangsan5");
        sc.add("zhangsan6");

        System.out.println(sc.getFirst()); // zhangsan1
        System.out.println(sc.getLast()); // zhangsan6
    }

    /**
     * 翻转集合中的元素
     */
    @Test
    public void testReversed() {
        SequencedCollection<String> sc = new ArrayList<>();
        sc.add("zhangsan1");
        sc.add("zhangsan2");
        sc.add("zhangsan3");
        sc.add("zhangsan4");
        sc.add("zhangsan5");
        sc.add("zhangsan6");

        SequencedCollection<String> reversed = sc.reversed();

        Iterator<String> iterator = reversed.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }
    }
}

6. 泛型(Java5)

6.1 初识泛型

package addery.zeus.com.genericitytest;

public class User {
    private String name;

    public User(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

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

    public void print() {
        System.out.println(this.name + "正在打印");
    }
}
package addery.zeus.com.genericitytest;

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

public class GenericityTest02 {
    public static void main(String[] args) {
        // 如果不使用泛型
        //Collection collection = new ArrayList();

         Collection<User> collection = new ArrayList<User>();
        // 钻石表达式 省略后面泛型的类型
        //Collection<User> collection = new ArrayList<>();

        User user1 = new User("zhangsan");
        User user2 = new User("wangwu");
        collection.add(user1);
        collection.add(user2);

        //Iterator iterator = collection.iterator();
        Iterator<User> iterator = collection.iterator();
        while(iterator.hasNext()) {
            // 不使用泛型 只能做向下转型
            //Object next = iterator.next();
            //User next1 = (User) next;
            //next1.print();

            User next = iterator.next();
            next.print();

        }
    }
}
package addery.zeus.com.genericitytest;

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

public class GenericityTest01 {
    public static void main(String[] args) {
        // 指定了集合中存储元素的类型
        Collection<String> collection = new ArrayList<String>();

        // 向集合中添加元素
        collection.add("addery");
        collection.add("zeus");
        // 编译器报错 只能向集合中添加String类型元素
        //collection.add(10);

        // 遍历集合
        Iterator<String> iterator = collection.iterator();
        while(iterator.hasNext()) {
            String next = iterator.next();
            // 使用String类型的方法
            System.out.println(next.charAt(1));
        }

    }
}

6.2 泛型的擦除与补偿

6.3 泛型的定义

6.3.1 在类上定义泛型
package addery.zeus.com.genericitytest;

public class Student <S> {
    private S name;

    public S getName() {
        return name;
    }

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

    public Student() {
    }

    public Student(S name) {
        this.name = name;
    }
}
package addery.zeus.com.genericitytest;

public class GenericityTest03 {
    public static void main(String[] args) {
        Student<String> stringStudent = new Student<String>();

        stringStudent.setName("zhangsan");
        String name = stringStudent.getName();
        // 编译器报错
        //stringStudent.setName(10);
    }
}

package addery.zeus.com.genericitytest;

public class Animals <N, A>{
    private N name;
    private A age;

    public Animals() {
    }

    public Animals(N name, A age) {
        this.name = name;
        this.age = age;
    }

    public N getName() {
        return name;
    }

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

    public A getAge() {
        return age;
    }

    public void setAge(A age) {
        this.age = age;
    }
}
package addery.zeus.com.genericitytest;

public class GenericityTest04 {
    public static void main(String[] args) {
        Animals<String, Integer> an = new Animals<>();

        an.setAge(10);
        Integer age = an.getAge();
        an.setName("Zeus");
        String name = an.getName();
    }
}
6.3.2 在静态方法上定义泛型

在类名上的泛型都实在实例化对象时使用,或者说初始化的,但对于静态方法不涉及对象所以无法使用类名上的泛型

package addery.zeus.com.genericitytest;

public class GenericityTest05{
    
    /*
    如果在静态方法中使用泛型,则需要在方法返回值类型前面进行泛型的声明。
    public static <E> void eatable(E e) {

    }
     */

    public static <E> void eatable(E[] es) {
        for (E e : es) {
            System.out.println(e);
        }
    }
    

    public static void main(String[] args) {
        String[] strs = {"zzy", "addery", "zeus"};
        eatable(strs);
    }
}
6.3.3 在接口上定义泛型
package addery.zeus.com.genericitytest;

public interface Flyable <T>{
    void fly(T t);
}
package addery.zeus.com.genericitytest;

// 知道具体的类型
public class Bird implements Flyable<Bird>{
    @Override
    public void fly(Bird bird) {

    }
}
package addery.zeus.com.genericitytest;

// 不知道具体的类型
public class Plane <A> implements Flyable<A>{
    @Override
    public void fly(A a) {

    }
}

接口上使用泛型的区别

package addery.zeus.com.genericitytest;

public interface MyComparable<C> {
    int compareTo(C c);
}
package addery.zeus.com.genericitytest;

/**
 * 实现接口不使用泛型,方法里的参数列表类型默认为Object,进行比较的时候需要强制类型转化
 */
public class Goods implements MyComparable{
    private int price;

    public Goods() {
    }

    public Goods(int price) {
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public int compareTo(Object o) {
        Goods goo = (Goods) o;
        return this.price - goo.price;

    }
}
package addery.zeus.com.genericitytest;

/**
 * 使用泛型不需要再手动进行强制类型转化
 */
public class Goods1 implements MyComparable<Goods1>{
    private int price;

    public Goods1() {
    }

    public Goods1(int price) {
        this.price = price;
    }

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public int compareTo(Goods1 goo) {
        return this.price - goo.price;
    }
}

6.4 泛型通配符

package addery.zeus.com.genericitytest;

import java.math.BigDecimal;
import java.util.ArrayList;

public class GenericityTest06 {

    public static void print1(ArrayList<?> arrayList) {
        // 任意引用类型
    }

    public static void print2(ArrayList<? extends Number> arrayList) {
        // Number及Number的子类
    }

    public static void print3(ArrayList<? super B> arrayList) {
        // B和B的父类
    }

    public static void main(String[] args) {
        print1(new ArrayList<String>());
        print1(new ArrayList<Integer>());
        print1(new ArrayList<Object>());

        print2(new ArrayList<Number>());
        print2(new ArrayList<BigDecimal>());
        print2(new ArrayList<Double>());
        print2(new ArrayList<Integer>());
        // 编译器报错
        //print2(new ArrayList<Object>());

        print3(new ArrayList<B>());
        print3(new ArrayList<A>());
        print3(new ArrayList<Object>());
        // 编译器报错
        //print3(new ArrayList<C>());
    }
}

class A {

}
class B extends A {

}

class C extends B {

}

7. 迭代时删除元素

package addery.zeus.com.genericitytest;

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

/**
 * 迭代时删除元素
 */
public class GenericityTest07 {
    public static void main(String[] args) {
        // 创建集合对象
        Collection<String> c = new ArrayList<>();

        // 向集合中添加元素
        c.add("zhangsan");
        c.add("wangwu");
        c.add("lisi");
        c.add("wangmazi");

        // 遍历集合
        Iterator<String> it = c.iterator();
        while (it.hasNext()) {
            String name = it.next();
            if ("lisi".equals(name)) {
                // 使用Collection的remove()方法
                // ConcurrentModificationException异常。
                //c.remove(name); 
                
                // 使用迭代器的remove()方法
                it.remove();
            }
            System.out.println(name);
        }
    }
}

8. List接口

8.1 常用方法

package addery.zeus.com.listtest;

import org.junit.jupiter.api.Test;

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

/**
 * List接口常用方法单元测试
 *
 * void add(int index, E element) 在指定索引处插入元素
 * E set(int index, E element); 修改索引处的元素
 * E get(int index); 根据索引获取元素(通过这个方法List集合具有自己特殊的遍历方式:根据下标遍历)
 * E remove(int index); 删除索引处的元素
 * int indexOf(Object o); 获取对象o在当前集合中第一次出现时的索引。
 * int lastIndexOf(Object o); 获取对象o在当前集合中最后一次出现时的索引。
 * List<E> subList(int fromIndex, int toIndex); 截取子List集合生成一个新集合(对原集合无影响)。[fromIndex, toIndex)
 *
 * static List<E> of(E... elements); 静态方法,返回包含任意数量元素的不可修改列表。(获取的集合是只读的,不可修改的。)
 */
public class ListMethodTest {
    // void add(int index, E element) 在指定索引处插入元素
    @Test
    public void testAdd() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        list.add(1, "李四");

        Iterator<String> it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        System.out.println(list); // [zhangsan, 李四, lisi, wangwu, zhaoliu]
    }

    // E set(int index, E element); 修改索引处的元素
    @Test
    public void testSet() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        list.set(1, "李四");

        System.out.println(list); // [zhangsan, 李四, wangwu, zhaoliu]
    }

    // E remove(int index); 删除索引处的元素
    // E get(int index); 根据索引获取元素(通过这个方法List集合具有自己特殊的遍历方式:根据下标遍历)
    @Test
    public void testRemoveAndGet() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        for (int i = 0; i < list.size(); i++) {
            if ("lisi".equals(list.get(i))) {
                list.remove(i);
            }
        }

        System.out.println(list);
    }

    // int indexOf(Object o); 获取对象o在当前集合中第一次出现时的索引。
    // int lastIndexOf(Object o); 获取对象o在当前集合中最后一次出现时的索引。
    @Test
    public void testIndexOfAndLastIndexOf() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");
        list.add("zhangsan");

        System.out.println(list.indexOf("zhangsan")); // 0
        System.out.println(list.lastIndexOf("zhangsan")); // 4
    }

    // List<E> subList(int fromIndex, int toIndex); 截取子List集合生成一个新集合(对原集合无影响)。
    // [fromIndex, toIndex)
    @Test
    public void testSubList() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        List<String> subList = list.subList(1, 3); // [1, 3)
        System.out.println(subList); // [lisi, wangwu]
    }

    // static List<E> of(E... elements); 静态方法,返回包含任意数量元素的不可修改列表。(获取的集合是只读的,不可修改的。)
    @Test
    public void testOf() {
        List<String> list = List.of("1", "2", "3", "Zues");
        System.out.println(list);
        // 只能读不能修改
        //list.set(1, "Addery"); // UnsupportedOperationException
    }
}

8.2 特有迭代器

package addery.zeus.com.listtest;

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

// List接口特有迭代器
public class ListTest01 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        // 获取List集合特有的迭代器
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

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

        // 从列表中的指定位置开始,获取List集合特有的迭代器
        ListIterator<String> it1 = list.listIterator(1);
        while (it1.hasNext()) {
            System.out.println(it1.next());
        }
    }
}

package addery.zeus.com.listtest;

import org.junit.jupiter.api.Test;

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

/**
* ListIterator接口常用方法单元测试
*
*   boolean hasNext(); 		判断光标当前指向的位置是否存在元素。
*   E next();				将当前光标指向的元素返回,然后将光标向下移动一位。
*   void remove();			删除上一次next()方法返回的那个数据(删除的是集合中的)。remove()方法调用的前提是:你先调用next()方法。不然会报错。*
*
*   void add(E e);			添加元素(将元素添加到光标指向的位置,然后光标向下移动一位。)
*   boolean hasPrevious();	判断当前光标指向位置的上一个位置是否存在元素。
*   E previous();			获取上一个元素(将光标向上移动一位,然后将光标指向的元素返回)
*   int nextIndex();		获取光标指向的那个位置的下标
*   int previousIndex();	获取光标指向的那个位置的上一个位置的下标
*   void set(E e);			修改的是上一次next()方法返回的那个数据(修改的是集合中的)。set()方法调用的前提是:你先调用了next()方法。不然会报错。
*/
public class ListIteratorMethodTest {
    // void add(E e); 添加元素(将元素添加到光标指向的位置,然后光标向下移动一位。)
    @Test
    public void testAdd() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            String next = it.next();
            if ("lisi".equals(next)) {
                it.add("李四"); // 添加后,光标向下移动一位,指向wangwu
            }

            System.out.println(next); // zhangsan, lisi, wangwu, zhaoliu 不会输出李四
        }

        System.out.println(list); // [zhangsan, lisi, 李四, wangwu, zhaoliu]
    }

    // boolean hasPrevious(); 判断当前光标指向位置的上一个位置是否存在元素。
    // E previous(); 获取上一个元素(将光标向上移动一位,然后将光标指向的元素返回)
    @Test
    public void testHasPreviousAndPrevious() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        ListIterator<String> it = list.listIterator();

        System.out.println(it.hasPrevious()); // false

        while (it.hasNext()) {
            System.out.println(it.next());
        }

        System.out.println(it.hasPrevious()); // true
        System.out.println(it.previous()); // zhaoliu
    }

    // int nextIndex();	获取光标指向的那个位置的下标
    // int previousIndex();	获取光标指向的那个位置的上一个位置的下标
    @Test
    public void testNextIndexAndPreviousIndex() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        ListIterator<String> it = list.listIterator();
        while (it.hasNext()) {
            String next = it.next();
            if ("lisi".equals(next)) {
                System.out.println(it.nextIndex()); // 2
                System.out.println(it.previousIndex()); // 1
            }
        }
    }

    // void set(E e); 修改的是上一次next()方法返回的那个数据(修改的是集合中的)。
    // set()方法调用的前提是:你先调用了next()方法。不然会报错。
    @Test
    public void testSet() {
        List<String> list = new ArrayList<>();

        list.add("zhangsan");
        list.add("lisi");
        list.add("wangwu");
        list.add("zhaoliu");

        ListIterator<String> it = list.listIterator();
        //it.set("addery"); // IllegalStateException

        while (it.hasNext()) {
            String next = it.next();
            if ("lisi".equals(next)) {
                it.set("李四");
            }
        }

        System.out.println(list); // [zhangsan, 李四, wangwu, zhaoliu]
    }

}

8.3 使用Comparator排序

  • 数组排序(要排序的类实现Comparable接口)
package addery.zeus.com.listtest.arraysort;

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

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public int compareTo(User o) {
        return this.getAge() - o.getAge();
    }
}
package addery.zeus.com.listtest.arraysort;

import java.util.Arrays;

public class UserSort {
    public static void main(String[] args) {
        User user1 = new User("abc", 10);
        User user2 = new User("abb", 8);
        User user3 = new User("bbc", 16);
        User user4 = new User("bcc", 13);
        User[] users = {user1, user2, user3, user4};
        Arrays.sort(users);

        System.out.println(Arrays.toString(users));
    }
}
  • List集合的排序:Comparator方式
package addery.zeus.com.listtest.listsort;

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

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

    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;
    }
}
package addery.zeus.com.listtest.listsort;

import java.util.Comparator;

public class PersonComparator implements Comparator<Person> {
    @Override
    public int compare(Person o1, Person o2) {
        return o1.getAge() - o2.getAge();
    }
}
package addery.zeus.com.listtest.listsort;

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

public class PersonSort {
    public static void main(String[] args) {
        Person p1 = new Person("abc", 10);
        Person p2 = new Person("abb", 8);
        Person p3 = new Person("bbc", 16);
        Person p4 = new Person("bcc", 13);

        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);

        list.sort(new PersonComparator());

        System.out.println(list);
    }
}
  • List集合的排序:Comparator方式 + 匿名内部类
package addery.zeus.com.listtest.listsort;

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

public class PersonSort1 {
    public static void main(String[] args) {
        Person p1 = new Person("abc", 10);
        Person p2 = new Person("abb", 8);
        Person p3 = new Person("bbc", 16);
        Person p4 = new Person("bcc", 13);

        List<Person> list = new ArrayList<>();
        list.add(p1);
        list.add(p2);
        list.add(p3);
        list.add(p4);

        list.sort(new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getName().compareTo(o2.getName());
            }
        });

        System.out.println(list);
    }
}

9. ArrayList


  1. 默认初始化容量**(0)**

  2. 第一次调用add()方法,底层扩容为长度为10的数组

  3. 扩容策略

    扩容之后的容量是原来容量的1.5倍

10. Vector

  1. 调用无参构造器,Vector默认初始化长度为10

  2. 扩容策略,扩大为原来的2倍

11. 链表存储结构

12. LinkedList

  1. 调用无参构造器,初始化长度为0

  2. 调用add(“Zeus”)方法默认插入到链表的尾部

  3. 调用add()方法插入到指定位置

  4. 修改元素

    检查下标是否越界—>找到待修改的结点—>修改—>返回修改前的结点数据

  5. 删除元素

    上述代码中如果删除的是头结点直接first = next;,不需要将next.prev置空吗?

    对于头节点的删除操作,确实没有单独的步骤去将新头节点的prev置为null,因为链表的结构维护逻辑已经隐含地处理了这种情况,确保了链接的正确性。

  6. 获取元素

13. 手写单向链表

13.1 单链表类

package addery.zeus.com.listtest;

/**
 * 单向链表
 */
public class MyLinkedList<E> {
    /**
     * 链表中结点的个数
     */
    private int size;

    /**
     * 链表的头结点
     */
    private Node<E> first;

    public MyLinkedList() {
    }

    /**
     * 默认的添加方式
     * @param data 待添加的数据
     */
    public boolean add(E data) {
        addLast(data);
        return true;
    }

    /**
     * 添加至指定位置
     * @param index 待添加的位置
     * @param data 待添加的数据
     */
    public void add(int index, E data) {
        checkPositionIndex(index);

        if (index == size)
            addLast(data);
        else
            addAppoint(data, node(index), index);
    }

    /**
     * 删除指定索引处的结点,并返回原结点的数据
     * @param index 待删除的索引
     * @return 待删除索引的数据
     */
    public E remove(int index) {
        checkPositionIndex(index);

        Node<E> oldNode = node(index);
        E oldNodeData = oldNode.item;


        if (index == 0) { // 如果删除的是头结点
            first = oldNode.next;
        }else { // 非头结点
            Node<E> oldNodePrev = node(index - 1);
            oldNodePrev.next = oldNode.next;
        }

        oldNode.next = null;
        oldNode.item = null;
        size--;

        return oldNodeData;
    }

    /**
     * 修改指定索引处结点的数据,并返回原始数据
     * @param index 待修改的结点索引
     * @param data 新的结点数据
     * @return 待修改结点的原始数据
     */
    public E set(int index, E data) {
        checkPositionIndex(index);

        Node<E> oldNode = node(index);
        E oldData = oldNode.item;
        oldNode.item = data;

        return oldData;
    }

    /**
     * 获取指定结点处的元素数据
     * @param index 待查找的索引
     * @return 待查找索引的数据
     */
    public E get(int index) {
        checkPositionIndex(index);
        return node(index).item;
    }

    /**
     * 默认添加至末尾
     */
    public void addLast(E data) {
        Node<E> newNode = new Node<>(data, null);

        if (first == null)
            first = newNode;
        else
            node(size - 1).next = newNode;

        size++;
    }

    /**
     * 添加结点至指定位置
     * @param data 待添加的结点数据
     * @param in 目标位置的结点对象
     * @param index 目标位置索引
     */
    void addAppoint(E data, Node<E> in, int index) {
        Node<E> newNode = new Node<>(data, in);

        if (index == 0) { // 如果在头结点处添加结点
            first = newNode;
        } else { // 非头结点处
            Node<E> inPrevNode = node(index - 1);
            inPrevNode.next = newNode;
        }

        size++;
    }

    /**
     * 查找结点,返回结点对象
     * @param index 待查找到结点索引
     * @return 目标节点对象
     */
    Node<E> node(int index) {
        Node<E> x = first;
        for(int i = 0; i < index; i++) {
            x = x.next;
        }
        return x;
    }

    /**
     * 检查下标
     * @param index 下标
     */
    private void checkPositionIndex(int index) {
        if (!isPositionIndex(index))
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    /**
     * 下标是否在合理范围内
     * @param index 下标
     * @return 合理true,不合理false
     */
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }

    /**
     * 构造IndexOutOfBoundsException的提示信息
     * @param index 下标
     * @return 提示信息:String
     */
    private String outOfBoundsMsg(int index) {
        return "Index:" + index + ", Size:" + size;
    }

    /**
     * 获取链表中结点的个数
     * @return 结点个数
     */
    public int getSize() {
        return size;
    }

    /**
     * 内部类:单链表的结点
     */
    private static class Node<E> {
        /**
         * 结点数据
         */
        E item;
        /**
         * 下一个结点
         */
        Node<E> next;

        Node(E item, Node<E> next) {
            this.item = item;
            this.next = next;
        }
    }
}

13.2 Junit单元测试

package addery.zeus.com.listtest;

import org.junit.jupiter.api.Test;

/**
 * MyLinkedList自定义单链表方法单元测试
 */
public class MyLinkedListMethodTest {
    // add()
    @Test
    public void testAdd() {
        MyLinkedList<String> mll = new MyLinkedList<>();
        mll.add("Zeus");
        mll.add("Stone");
        mll.add("Apple");
        mll.add("pear");
        mll.add("peach");

        mll.add(2,  "Addery");
        //mll.add(10,  "Addery"); // java.lang.IndexOutOfBoundsException: Index:10, Size:6

        for (int i = 0; i < mll.getSize(); i++) {
            System.out.println(mll.get(i));
        }

    }

    // test()
    @Test
    public void testSet() {
        MyLinkedList<String> mll = new MyLinkedList<>();
        mll.add("Zeus");
        mll.add("Stone");
        mll.add("Apple");
        mll.add("pear");
        mll.add("peach");

        String oldData = mll.set(2, "Addery");
        System.out.println(oldData); // Apple

        for (int i = 0; i < mll.getSize(); i++) {
            System.out.println(mll.get(i));
        }

    }

    // remove()
    @Test
    public void testRemove() {
        MyLinkedList<String> mll = new MyLinkedList<>();
        mll.add("Zeus");
        mll.add("Stone");
        mll.add("Apple");
        mll.add("pear");
        mll.add("peach");

        String oldData = mll.remove(1);
        System.out.println(oldData);

        mll.remove(0);
        mll.add("z");

        for (int i = 0; i < mll.getSize(); i++) {
            System.out.println(mll.get(i));
        }
    }

    // get()
    @Test
    public void testGet() {
        MyLinkedList<String> mll = new MyLinkedList<>();
        mll.add("Zeus");
        mll.add("Stone");
        mll.add("Apple");
        mll.add("pear");
        mll.add("peach");

        System.out.println(mll.get(2));
        System.out.println("======");

        for (int i = 0; i < mll.getSize(); i++) {
            System.out.println(mll.get(i));
        }
    }
}

14. 栈

package addery.zeus.com.stacktest;

import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.Stack;

public class stackTest01 {
    public static void main(String[] args) {
        // 创建stack对象
        Stack<String> stack0 = new Stack<>();
        LinkedList<String> stack1 = new LinkedList<>();
        ArrayDeque<String> stack2 = new ArrayDeque<>();
        // 栈相关操作测试
        // 1. 压栈
        stack0.push("s");
        stack0.push("z");
        stack0.push("y");
        System.out.println(stack0.search("z")); // 2

        stack1.push("1");
        stack1.push("2");
        stack1.push("3");

        stack2.push("1");
        stack2.push("2");
        stack2.push("3");

        // 2.弹栈,将栈顶元素删除,并返回被删除的引用
        System.out.println(stack0.pop()); // y
        System.out.println(stack1.pop()); // 3
        System.out.println(stack2.pop()); // 3

        // 窥视栈顶元素
        System.out.println(stack0.peek()); // z

        // 查找栈中元素
        System.out.println(stack0.search("z")); // 1,以1开始,从栈顶元素往下数

    }
}

15. 队列

package addery.zeus.com.queuetest;

import java.util.ArrayDeque;
import java.util.LinkedList;
import java.util.Queue;

/**
 * Queue接口基于Collection扩展的方法包括:
 * boolean offer(E e); 入队。
 * E poll(); 出队,如果队列为空,返回null。
 * E remove(); 出队,如果队列为空,抛异常。
 * E peek();	查看队头元素,如果为空则返回null。
 * E element(); 查看对头元素,如果为空则抛异常。
 */
public class queueTest01 {
    public static void main(String[] args) {
        // 创建队列对象
        Queue<String> queue1 = new LinkedList<>();
        Queue<String> queue2 = new ArrayDeque<>();

        // 入队
        queue1.offer("element1");
        queue1.offer("element2");
        System.out.println(queue1.offer("element3"));

        queue2.offer("element1");
        queue2.offer("element2");
        System.out.println(queue2.offer("element3"));

        // 出队
        System.out.println("--------------LinkedList------------");
        String poll = queue1.poll();
        System.out.println(poll); // element1
        System.out.println(queue1.poll()); // element2
        System.out.println("--------------ArrayDeque------------");
        System.out.println(queue2.poll());
        System.out.println(queue2.poll());
        System.out.println(queue2.poll());
//        System.out.println(queue2.poll()); // null
//        System.out.println(queue2.remove()); // NoSuchElementException

        // peek()
        System.out.println("--------------peek()------------");
        System.out.println(queue1.peek());
        System.out.println(queue2.peek()); // null

        // element()
        System.out.println(queue2.element()); // NoSuchElementException

    }
}
package addery.zeus.com.queuetest;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.LinkedList;

/**
 * boolean offerLast(E e); 	从队尾入队
 * E pollFirst(); 			从队头出队
 * boolean offerFirst(E e); 	从队头入队
 * E pollLast(); 			从队尾出队
 */
public class dequeTest01 {
    public static void main(String[] args) {
        // 创建双端队列
        Deque<String> deque1 = new LinkedList<>();
        Deque<String> deque2 = new ArrayDeque<>();

        // 从队尾进,从队头出
        System.out.println("------------从队尾进,从队头出--------------");
        deque1.offer("z1");
        deque1.offer("z2");
        deque1.offer("z3");
        System.out.println(deque1.poll());
        System.out.println(deque1.poll());
        System.out.println(deque1.poll());

        System.out.println("------------从队尾进,从队头出--------------");
        deque1.offerLast("a1");
        deque1.offerLast("a2");
        deque1.offerLast("a3");
        System.out.println(deque1.pollFirst());
        System.out.println(deque1.pollFirst());
        System.out.println(deque1.pollFirst());

        // 从队头进,从队尾出
        System.out.println("-------------从队头进,从队尾出-----------------");
        deque1.offerFirst("f1");
        deque1.offerFirst("f2");
        deque1.offerFirst("f3");
        System.out.println(deque1.pollLast());
        System.out.println(deque1.pollLast());
        System.out.println(deque1.pollLast());

        // 从队尾进,从队尾出 模拟栈
        System.out.println("------------从队尾进,从队尾出--------------");
        deque2.offerLast("ff1");
        deque2.offerLast("ff2");
        deque2.offerLast("ff3");
        System.out.println(deque2.pollLast());
        System.out.println(deque2.pollLast());
        System.out.println(deque2.pollLast());
    }
}

16. Set

底层都是用Map实现的

17. Map

17.1 Map继承结构

17.2 Map接口的常用方法

package addery.zeus.com.maptest;

import org.junit.jupiter.api.Test;

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

/**
 * V put(K key, V value);				添加键值对
 * void putAll(Map<? extends K,? extends V> m);	添加多个键值对
 * V get(Object key);					通过key获取value
 * boolean containsKey(Object key);			是否包含某个key
 * boolean containsValue(Object value);		是否包含某个value
 * V remove(Object key);					通过key删除key-value
 * void clear();						清空Map
 * int size();							键值对个数
 * boolean isEmpty();					判断是否为空Map
 * Collection<V> values();				获取所有的value
 * Set<K> keySet();						获取所有的key
 * Set<Map.Entry<K,V>> entrySet();			获取所有键值对的Set视图。
 * static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3);	静态方法,使用现有的key-value构造Map
 */
public class MapMethodTest {
    // V put(K key, V value);				添加键值对
    // void putAll(Map<? extends K,? extends V> m);	添加多个键值对
    @Test
    public void testPutAndPutAll() {
        Map<Integer, String> map = new HashMap<>();
        System.out.println("-----------put------------");
        map.put(1, "zeus");
        map.put(2, "zzy");
        System.out.println(map);

        System.out.println("----------putAll-----------");
        Map<Integer, String> map1 = new HashMap<>();
        map1.put(3, "test");
        map1.putAll(map);
        System.out.println(map1);

        System.out.println("-----Map<? extends K,? extends V> m-----");

    }

    // V get(Object key);					通过key获取value
    @Test
    public void testGet() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        System.out.println(map.get(1)); // zeus
    }

    //  * boolean containsKey(Object key);			是否包含某个key
    // * boolean containsValue(Object value);		是否包含某个value
    @Test
    public void testContainsKeyAndContainsValue() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        map.put(3, "stone");
        System.out.println("------------containsKey-------------");
        System.out.println(map.containsKey(1));
        System.out.println(map.containsKey(4));
        System.out.println("------------containsValue-------------");
        System.out.println(map.containsValue("zzy"));
        System.out.println(map.containsValue("aiohfo"));
    }

    //  * V remove(Object key);					通过key删除key-value
    // * void clear();						清空Map
    // boolean isEmpty();					判断是否为空Map
    @Test
    public void testRemoveAndClearAndIsEmpty() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        map.put(3, "stone");
        System.out.println(map.remove(1)); // 返回值为Value:zeus
        System.out.println(map);

        System.out.println(map.isEmpty()); // false

        map.clear(); // {2=zzy, 3=stone}
        System.out.println(map); // {}

        System.out.println(map.isEmpty()); // true
    }

    // int size();							键值对个数
    @Test
    public void testSize() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        System.out.println(map.size()); // 2
        map.put(3, "stone");
        System.out.println(map.size()); // 3
    }

    //  * Collection<V> values();				获取所有的value
    // * Set<K> keySet();						获取所有的key
    @Test
    public void testValuesAndKeySet() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        map.put(3, "stone");
        System.out.println("values:" + map.values());
        System.out.println("key:" + map.keySet());
    }

    // * static <K,V> Map<K,V> of(K k1, V v1, K k2, V v2, K k3, V v3);	静态方法,使用现有的key-value构造Map
    @Test
    public void testMap() {
        Map<Integer, String> map = Map.of(1, "z", 2, "z", 3, "y");
        System.out.println(map);
    }

    //  * Set<Map.Entry<K,V>> entrySet();			获取所有键值对的Set视图。
    @Test
    public void testEntrySet() {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "zeus");
        map.put(2, "zzy");
        map.put(3, "stone");
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        System.out.println(entries);
    }

}

17.3 Map集合的遍历方法

package addery.zeus.com.maptest;

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

/**
 * 遍历map集合的方法
 */
public class MapTest01 {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "z");
        map.put(2, "y");
        map.put(4, "d");
        map.put(3, "g");

        // 第一种方式,拿到map集合所有的key,通过key去找value
        System.out.println("----第一种方式----");
        Set<Integer> keys = map.keySet();
        Iterator<Integer> it = keys.iterator();
        while (it.hasNext()) {
            Integer key = it.next();
            System.out.println(map.get(key));
        }

        System.out.println("----foreach----");
        for (Integer key : keys) {
            System.out.println(map.get(key));
        }

        // 第二种方式通过entrySet()方法
        System.out.println("----第二种方法entrySet---");
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        Iterator<Map.Entry<Integer, String>> iterator = entries.iterator();
        while (iterator.hasNext()) {
            Map.Entry<Integer, String> next = iterator.next();
            Integer key = next.getKey();
            String value = next.getValue();
            System.out.println(key + "=" + value);
        }

        System.out.println("----foreach----");
        for (Map.Entry<Integer, String> en : entries) {
            System.out.println(en.getKey() + "=" + en.getValue());
        }

    }
}

18. HashMap

18.1 概述

18.2 存储原理

18.3 手写put()和get()方法

package addery.zeus.com.hashmap;


/**
 * 手写HashMap类的put()和get()方法
 */
public class MyHashMap<K, V> {
    /**
     * 哈希表
     */
    private Node<K, V>[] table;

    /**
     * 哈希表的长度
     */
    private int size;

    @SuppressWarnings("unchecked")
    public MyHashMap() {
        // 在new数据的时候不能使用泛型,new Node<V, K>[16]
        this.table = new Node[16];
    }

    public int getSize() {
        return size;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < table.length; i++) {
            Node<K, V> node = table[i];
            // 如果table[i]不为空,遍历整个单链表
            while (null != node) {
                sb.append(node);
                sb.append(", ");
                node = node.next;
            }
        }
        return sb.toString();
    }

    static class Node<K, V> {
        /**
         * 值
         */
        private V value;
        /**
         * 键
         */
        private K key;
        /**
         * 哈希值
         */
        private int hash;
        /**
         * 下一个结点
         */
        private Node<K, V> next;

        public Node() {
        }

        public Node(V value, K key, int hash, Node<K, V> next) {
            this.value = value;
            this.key = key;
            this.hash = hash;
            this.next = next;
        }

        public V getValue() {
            return value;
        }

        public void setValue(V value) {
            this.value = value;
        }

        public K getKey() {
            return key;
        }

        public void setKey(K key) {
            this.key = key;
        }

        public int getHash() {
            return hash;
        }

        public void setHash(int hash) {
            this.hash = hash;
        }

        public Node<K, V> getNext() {
            return next;
        }

        public void setNext(Node<K, V> next) {
            this.next = next;
        }

        @Override
        public String toString() {
            return "[" + "value=" + value + ", key=" + key + "]";

        }
    }

    /**
     * 【第一步】:处理key为null的情况
     * 如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。
     * 【第二步】:获得key对象的哈希值
     * 如果添加键值对的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
     * 【第三步】:获得键值对的存储位置
     * 因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
     * 那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
     * 【第四步】:将键值对添加到table数组中
     * 当table[i]返回结果为null时,则键键值对封装为Node对象并存入到table[i]的位置。
     * 当table[i]返回结果不为null时,则意味着table[i]存储的是单链表。我们首先遍历单链表,如果遍历出来节点的
     * key和添加键值对的key相同,那么就执行覆盖操作;如果遍历出来节点的key和添加键值对的key都不同,则就将键键
     * 值对封装为Node对象并插入到单链表末尾。
     *
     * @param value 值
     * @param key   键
     */
    public V put(K key, V value) {
        // 如果添加键值对的key就是null,则将该键值对存储到table数组索引为0的位置。
        if (null == key) {
            return addForNullKey(value);
        }

        // 如果key不是null
        int code = key.hashCode();
        int tableIndex = Math.abs(code % table.length);
        // 如果table[tableIndex]还没有数据,直接添加
        Node<K, V> node = table[tableIndex];
        if (null == node) {
            Node<K, V> newNode = new Node<>(value, key, code, null);
            table[tableIndex] = newNode;
            size++;
            return value;
        }
        // 如果程序执行到这里,说明table[tableIndex]存在数据,则需要遍历链表
        Node<K, V> preNode = null;
        while (null != node) {
            if (key.equals(node.getKey())) {
                V oldValue = node.getValue();
                node.setValue(value);
                return oldValue;
            }
            preNode = node;
            node = node.next;
        }
        Node<K, V> newNode = new Node<>(value, key, code, null);
        preNode = newNode;
        size++;
        return value;

    }

    /**
     * key为null的情况
     *
     * @param value 值
     * @return oldValue或者value
     */
    private V addForNullKey(V value) {
        // 如果table[0]还没有数据,直接添加
        Node<K, V> node = table[0];
        if (null == node) {
            Node<K, V> newNode = new Node<>(value, null, 0, null);
            table[0] = newNode;
            size++;
            return value;
        }
        // 如果程序执行到这里,说明table有单链表,需要遍历单链表,寻找key为null的结点
        Node<K, V> preNode = null;
        while (null != node) {
            if (null == node.getKey()) {
                V oldValue = node.getValue();
                node.setValue(value);
                return oldValue;
            }
            preNode = node;
            node = node.next;
        }
        // 如果程序执行到这里,说明table[0]存在单链表,但是链表中不存在key为null的结点
        Node<K, V> newNode = new Node<>(value, null, 0, null);
        preNode.next = newNode;
        size++;
        return value;
    }

    /**
     * 【第一步】:处理key为null的情况
     *      如果查询的key就是null,则就在table数组索引为0的位置去查询。
     * 【第二步】:获得key对象的哈希值
     *      如果查询的key不是null,则就调用key的hashcode()方法,获得key的哈希值。
     * 【第三步】:获得键值对的存储位置
     *      因为获得的哈希值在数组合法索引范围之外,因此我们就需要将获得的哈希值转化为[0,数组长度-1]范围的整数,
     *      那么可以通过取模法来实现,也就是通过“哈希值 % 数组长度”来获得索引位置(i)。
     * 【第四步】:遍历单链表,根据key获得value值
     *      如果table[i]返回的结果为null,则证明单链表不存在,那么返回null即可
     *      如果table[i]返回的结果不为null时,则证明单链表存在,那么就遍历整个单链表。如果遍历出来节点的key和查询
     *      的key相同,那么就返回遍历出来节点的value值;如果整个单链表遍历完毕,则遍历出来节点的key和查询的key都不
     *      相等,那么就证明查询key在链表中不存在,则直接返回null即可。
     * @param key 键
     * @return 值
     */
    public V get(K key) {
        // 如果查询的key就是null,则就在table数组索引为0的位置去查询。
        if (null == key) {
            Node<K, V> node = table[0];
            if (null == node) {
                return null;
            }
            // 有单链表,遍历
            while(node != null) {
                if (null == node.getKey()) {
                    return node.getValue();
                }
                node = node.next;
            }
            return null;
        }

        // 如果key不是null
        int code = key.hashCode();
        int tableIndex = Math.abs(code % table.length);
        Node<K, V> node = table[tableIndex];
        // table[tableIndex]处不存在数据
        if (null == node) {
            return null;
        }
        // 如果程序运行到此处,说明table[tableIndex]处存在单链表
        while (null != node) {
            if (key.equals(node.getKey())) {
                return node.getValue();
            }
            node = node.next;
        }
        return null;

    }
}

package addery.zeus.com.hashmap;

import java.util.Objects;

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

    public User() {
    }

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }

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

package addery.zeus.com.hashmap;

/**
 * 手写HashMap的get和put方法测试程序
 */
public class HashMapTest01 {
    public static void main(String[] args) {
        System.out.println("------------put()------------");
        MyHashMap<String, String> map = new MyHashMap<>();
        map.put("张三", "111");
        map.put("李四", "222");
        map.put("王五", "333");
        map.put("赵六", "444");
        map.put("赵六", "555");
        map.put(null, "666");
        System.out.println(map);

        System.out.println("------------get()------------");
        System.out.println(map.get("张三"));
        System.out.println(map.get("赵六"));
        System.out.println(map.get(null));
        System.out.println(map.get("abc"));

        System.out.println("------------自定义数据类型------------");
        MyHashMap<User, Integer> userMap = new MyHashMap<>();
        User user1 = new User("张三", 18);
        User user2 = new User("李四", 18);
        User user3 = new User("王五", 18);
        User user4 = new User("赵六", 18);
        User user5 = new User("赵六", 19);
        User user6 = new User("赵六", 19);
        userMap.put(user1, 1);
        userMap.put(user2, 2);
        userMap.put(user3, 3);
        userMap.put(user4, 4);
        userMap.put(user5, 4);
        userMap.put(user6, 5);
        System.out.println(userMap);
    }
}

18.4 HashMap在Java8后的改进(包括Java8)

18.5 HashMap初始化容量


19. LinkedHashMap

package addery.zeus.com.linkedhashmaptest;

import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * 哈希表 + 双向链表
 */
public class LinkedHashMapTest01 {
    public static void main(String[] args) {
        LinkedHashMap<Integer, String> linkedHashMap = new LinkedHashMap<>();
        linkedHashMap.put(1, "a");
        linkedHashMap.put(4543, "b");
        linkedHashMap.put(222, "c");
        linkedHashMap.put(657, "d");
        linkedHashMap.put(888, "e");
        linkedHashMap.put(888, "eeee");

        Set<Map.Entry<Integer, String>> entries = linkedHashMap.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }
    }
}

20. Hashtable

package addery.zeus.com.hastabletest;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Map;
import java.util.Set;

/**
 * Hashtable是线程安全的
 * Hashtable初始化容量为11
 * Hashtable扩容也是2倍
 * Hashtable的key和value都不能为null,否则会空指针异常
 * Hashtable特有的方法
 *      Enumeration keys = hashtable.keys()
 *      Enumeration elements = hashtable.elements()
 * Enumeration的方法
 *      boolean has = enumeration.hasMoreElements()
 *      K/V e = enumeration.nextElement()
 */
public class HashtableTest01 {
    public static void main(String[] args) {
        Hashtable<Integer, String> map = new Hashtable<>();
        // NullPointerException
//        map.put(null, "zzz");
//        map.put(1, null);
//        map.put(null, null);
        map.put(1, "z");
        map.put(2, "z");
        map.put(3, "y");
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }

        Enumeration<Integer> keys = map.keys();
        while (keys.hasMoreElements()) {
            Integer i = keys.nextElement();
            System.out.println(i);
        }

        Enumeration<String> elements = map.elements();
        while (elements.hasMoreElements()) {
            String s = elements.nextElement();
            System.out.println(s);
        }
    }
}

21. Properties

package addery.zeus.com.propertiestest;

import java.util.Enumeration;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

/**
 * Properties不支持泛型,K和V只能是String类型
 * setProperties() (put())
 * getProperties() (get())
 * Enumeration<?> e = properties.propertyNames() (keys())
 */
public class PropertiesTest01 {
    public static void main(String[] args) {
        Properties properties = new Properties();
        properties.setProperty("zzy", "张志毅");
        properties.setProperty("zeus", "宙斯");

        System.out.println(properties.getProperty("zzy"));
        System.out.println(properties.getProperty("zeus"));

        Set<Map.Entry<Object, Object>> entries = properties.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            System.out.println(entry.getKey() + "=" + entry.getValue());
        }

        Enumeration<Object> keys = properties.keys();
        while (keys.hasMoreElements()) {
            String s = (String)keys.nextElement();
            System.out.println(s);
        }

        Enumeration<?> enumeration = properties.propertyNames();
        while (enumeration.hasMoreElements()) {
            String s = (String)enumeration.nextElement();
            System.out.println(s);
        }

    }
}

22. 二叉树

22.1. 排序二叉树

22.2. 平衡二叉树

22.3. 红黑二叉树(自平衡的排序二叉树)

23. TreeMap

package addery.zeus.com.treemaptest;

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

/**
 * TreeMap底层是红黑二叉树
 * TreeMap是有序不可重复的,有序是指 可排序的
 * TreeMap的k不允许为空
 */
public class TreeMapTest01 {
    public static void main(String[] args) {
        Map<Integer, String> treeMap = new TreeMap<>();
        treeMap.put(4, "4");
        treeMap.put(2, "2");
        treeMap.put(1, "1");
        treeMap.put(6, "6");
        treeMap.put(3, "3");
        treeMap.put(5, "5");
        treeMap.put(5, "55");

        System.out.println(treeMap);  // {1=1, 2=2, 3=3, 4=4, 5=55, 6=6}

        System.out.println("------------自定义数据类型------------");
        /**
         * java.lang.ClassCastException: class addery.zeus.com.treemaptest.User cannot be cast to class java.lang.Comparable
         * 自定义数据类型未实现comparable接口,重写compareTo方法,也没有在构造userTreeMap时传入Comparator对象会报类型转换异常
         * 这里可以看一小爱TreeMap的源码,在put元素的时候需要判断有无Comparator对象,如果没有会将put的对象转化为Comparable对象,在这里就会报出ClassCastException
         * 比如:如果第一次put会调用addEntryToEmptyMap(key, value)方法,在这个方法中会调用compare()方法,在这个方法中也是上述的判断逻辑
         */
        System.out.println("---方法1:使用实现Comparable接口的类型---");
        Map<User, String> userTreeMap = new TreeMap<>();
        User user1 = new User("张三", 18);
        User user2 = new User("李四", 16);
        User user3 = new User("王五", 19);
        User user4 = new User("赵六", 38);
        User user5 = new User("小红", 27);
        userTreeMap.put(user1, "1");
        userTreeMap.put(user2, "2");
        userTreeMap.put(user3, "3");
        userTreeMap.put(user4, "4");
        userTreeMap.put(user5, "5");
        System.out.println(userTreeMap);

        System.out.println("---方法2:使用Comparator比较器对象的类型---");
        VipComparator vipComparator = new VipComparator();
        Map<Vip, String> vipTreeMap = new TreeMap<>(vipComparator);
        Vip vip1 = new Vip("张三", 18);
        Vip vip2 = new Vip("李四", 16);
        Vip vip3 = new Vip("王五", 19);
        Vip vip4 = new Vip("赵六", 38);
        Vip vip5 = new Vip("小红", 27);
        vipTreeMap.put(vip1, "1");
        vipTreeMap.put(vip2, "2");
        vipTreeMap.put(vip3, "3");
        vipTreeMap.put(vip4, "4");
        vipTreeMap.put(vip5, "5");
        System.out.println(vipTreeMap);

        System.out.println("---方法3:使用Comparator比较器对象的类型,匿名内部类方式---");
        Map<Vip, String> vipTreeMap2 = new TreeMap<>(new Comparator<Vip>() {
            @Override
            public int compare(Vip o1, Vip o2) {
                return o1.getAge() - o2.getAge();
            }
        });
        Vip vipp1 = new Vip("张三", 18);
        Vip vipp2 = new Vip("李四", 16);
        Vip vipp3 = new Vip("王五", 19);
        Vip vipp4 = new Vip("赵六", 38);
        Vip vipp5 = new Vip("小红", 27);
        vipTreeMap2.put(vipp1, "1");
        vipTreeMap2.put(vipp2, "2");
        vipTreeMap2.put(vipp3, "3");
        vipTreeMap2.put(vipp4, "4");
        vipTreeMap2.put(vipp5, "5");
        System.out.println(vipTreeMap2);

        // TreeMap的k不允许为空 NullPointerException
//        vipTreeMap2.put(null, "5");
        vipTreeMap2.put(vipp5, null);
    }
}

class Vip {  /*---Vip类---*/
    private String name;
    private int 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;
    }

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

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Vip vip = (Vip) o;
        return age == vip.age && Objects.equals(name, vip.name);
    }

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

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

class VipComparator implements Comparator<Vip> {  /*---Vip类的比较器---*/
    @Override
    public int compare(Vip o1, Vip o2) {
        return o1.getAge() - o2.getAge();
    }
}
package addery.zeus.com.treemaptest;

import java.util.Objects;

/**
 * 用户类
 */
public class User implements Comparable<User> {
    private String name;
    private int age;

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

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return age == user.age && Objects.equals(name, user.name);
    }

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

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

    @Override
    public int compareTo(User o) {
        return this.age - o.age;
    }
}

24. Set接口

HashSet、LinkedHashSet、TreeSet

25. HashSet面试题

package addery.zeus.com.settest;

import java.util.HashSet;
public class HashSetTest02 {
    public static void main(String[] args) {
        // 创建一个存放Student类型数据的HashSet对象
        HashSet<Student> set = new HashSet<>();
        // 创建一个学生对象
        Student stu = new Student("张三", 18);
        // 向set中添加学生对象 张三 18
        set.add(stu);
        // 向set中添加学生对象 李四 21
        set.add(new Student("李四", 21));
        // 修改学生 stu的name 张三 ---> 王五
        stu.setName("王五");
        // 问题1:请问是否删除了HashSet集合中的stu对象呢???
        /**
         * 分析:HashSet集合中存放的是stu对象的引用,所以修改stu对象的name属性,在集合中也会被修改
         * 但是集合中修改的是的stu对象的name属性,并未修改其哈希值,因为哈希值是在该stu插入集合时生
         * 成的,而其哈希值的生成是根据name和age属性,所以其哈希值还是根据原始name 张三和age生成的
         * 结论:故此,remove(stu)时,会根据传入的Student类型对象的name和age属性计算哈希值,通
         * 过哈希值计算其再哈希表的所在位置,然后在链表中查找name为“王五”,age为“18”的Student对
         * 象,但由于其哈希值和根据新的name 王五 计算的,不一定为找到原来的位置,所以不会删除该对象
         */
        set.remove(stu);
        System.out.println(set);  // [Student{name='王五', age='18'}, Student{name='李四', age='21'}]
        // 问题2:添加以下Student对象是否成功???
        /**
         * 分析:根据问题1的分析,插入new Student("王五", 18)对象生成的哈希值和stu不同
         * 结论:所以会插入成功
         */
        set.add(new Student("王五", 18));
        System.out.println(set);  // [Student{name='王五', age='18'}, Student{name='王五', age='18'}, Student{name='李四', age='21'}]
        // 问题3:添加以下Student对象是否成功???
        /**
         * 分析:基于问题1的分析,插入new Student("张三", 18)对象生成的哈希值和stu相同
         * 但是,哈希值相同只能说明在哈希表的相同索引处的链表中,在链表中还需要使用equals方法比较
         * 对象是否重复,我们的Student类中重写的equals是通过name和age共同判断的,所以这里equals
         * 方法返回False
         * 结论:会插入成功,并且在哈希表的相同位置
         */
        set.add(new Student("张三", 18));
        System.out.println(set);  // [Student{name='王五', age='18'}, Student{name='王五', age='18'}, Student{name='张三', age='18'}, Student{name='李四', age='21'}]

    }
}
package addery.zeus.com.settest;

import java.util.Objects;

public class Student {
    private String name;
    private int 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;
    }

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

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

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

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

}

26. Collections工具类

Collections工具类提供了很多操作集合的方法,如下图是几个比较常用的。

package addery.zeus.com.collectionstest;

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

/**
 * Collections工具类方法测试
 */
public class CollectionsTest01 {
    public static void main(String[] args) {
        System.out.println("---sort()---");
        List<Integer> list = Arrays.asList(6, 4, 2, 5, 1, 3);
        System.out.println(list);  // [6, 4, 2, 5, 1, 3]
        Collections.sort(list);
        System.out.println(list);  // [1, 2, 3, 4, 5, 6]

        System.out.println("---shuffle()打乱顺序---");
        Collections.shuffle(list);
        System.out.println(list);  // [4, 3, 6, 1, 2, 5]
        Collections.shuffle(list);
        System.out.println(list);  // [3, 1, 5, 6, 2, 4]

        System.out.println("---reverse()反转---");
        Collections.sort(list);
        System.out.println(list);  // [1, 2, 3, 4, 5, 6]
        Collections.reverse(list);
        System.out.println(list);  // [6, 5, 4, 3, 2, 1]

        System.out.println("---fill()替换所有元素---");
        Collections.fill(list, 666);
        System.out.println(list);  // [666, 666, 666, 666, 666, 666]

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值