Java 高级_集合*****

300 阅读30分钟

Java 集合

Java集合框架概述

一方面,为了方便对多个对象的操作,就要对对象进行存储;另一方面,使用 Array存储对象方面具有一些弊端,而 Java集合就像一种容器,可以 动态地 把多个对象的引用放入容器中

数组在存储多个数据方面的特点:

  • 初始化后,长度确定
  • 数组定义好,其元素的类型也就确定了

数组在存储多个数据方面的缺点:

  • 初始化后,其长度不可修改
  • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,且效率低
  • 数组存储的特点:有序、可重复;对于无序、不可重复的需求,不能满足

Java 集合可分为 Collection 和 Map 两种体系

  • Collection 接口:单列数据,定义了存取一组对象的方法的集合(用于存储一个一个的对象)

    1. List:元素有序、可重复的集合 ->(动态数组

      • ArrayListLinkedListVector
    2. Set:元素无序、不可重复的集合 ->(类似于高数的集合

      • HashSet、LinkedHashSet、TreeSet
  • Map(映射) 接口:双列数据,保存具有映射关系 “key-value” 对的集合 -> (高数函数

    • HashMap、LinkedHashMap、TreeMap、Hashtable、Propertise

Collection接口方法*****

向 Collection接口的实现类的对象中添加数据 obj时,要求 obj所在类要重写 equals()

  • add(Object obj)
  • addAll(Collection c1)
  • size()
  • clear()
  • isEmpty()
  • contains(Object obj):通过equals()进行比较,需要重写 equals()
package com.atguigu.java2;

import org.junit.Test;

import java.util.*;

/**
 * Collection
 * @author lv
 * @create 2020-12-20 16:57
 */
public class CollectionTest {
    @Test
    public void test4() {
        Collection c1 = new ArrayList();
        c1.add(456);
        c1.add(789);
        c1.add(123);
        c1.add(new Person("Tom", 21));

        // hashCode():返回当前对象的哈希值
        System.out.println(c1.hashCode()); // 595295220

        // 集合转换为数组:toArray()
        Object[] objArr = c1.toArray();
        System.out.println(objArr); // [Ljava.lang.Object;@78e03bb5
        System.out.println(Arrays.toString(objArr)); // [456, 789, 123, Person{name='Tom', age=21}]

        // 数组转为集合:Arrays.asList()
        // Arrays.asList(Object obj):当使用基本数据类型时,如 ints会被识别为一个元素
        Collection c2 = Arrays.asList(new String[]{"AAA", "BBB", "CCC"});
        System.out.println(c2); // [AAA, BBB, CCC]

        List ints = Arrays.asList(new int[]{1, 2, 3});
        System.out.println(ints.size() + " : " + ints); // 1 : [[I@5e8c92f4]

        List integers = Arrays.asList(new Integer[]{123, 456, 789});
        System.out.println(integers.size() + " : " + integers); // 3 : [123, 456, 789]

        // iterator():返回 Iterator接口的实例中,用于遍历集合元素,放在 java2.IteratorTest.java中
    }

    @Test
    public void test3() {
        Collection c1 = new ArrayList();
        c1.add(123);
        c1.add("789");
        c1.add(false);
        c1.add(new Person("Tom", 21));
        System.out.println(c1.add(456)); // true
        System.out.println(c1); // [123, 789, false, Person{name='Tom', age=21}, 456]

        Collection col11 = Arrays.asList(123, "789");

        // retainAll():(保留一样的,删除不同的)获取 当前集合和 col11的交集,并 返回给 当前集合
        c1.retainAll(col11);
        System.out.println(c1); // [123, 789]

        // equals(Object obj):集合中对应索引的内容和是否全部相同
        Collection col12 = Arrays.asList(123, "789");
        boolean isEquals = c1.equals(col12);
        System.out.println(isEquals); // true
    }
    @Test
    public void test2() {
        // remove(Object obj)
        Collection c1 = new ArrayList();
        c1.add(123);
        c1.add("789");
        c1.add(false);
        c1.add(new Person("Tom", 21));
        System.out.println(c1.add(456)); // true
        System.out.println(c1); // [123, 789, false, Person{name='Tom', age=21}, 456]

        boolean isRemove = c1.remove(123);
        c1.remove(new Person("Tom", 21));
        System.out.println(isRemove); // true
        System.out.println(c1); // [789, false, 456]

        // removeAll(Collection c)
        Collection col11 = Arrays.asList(123, 789, false);
        c1.removeAll(col11);
        System.out.println(c1); // [789, 456]

    }
    @Test
    public void test() {
        Collection col = new ArrayList();

        // add(Object obj),将元素 obj添加到集合 col中
        col.add("AA");
        col.add("BB");
        col.add(123); // 自动装箱
        col.add(new Date());

        // size(),获取添加的元素个数
        System.out.println(col.size()); // 4

        // addAll(Collection c1):将c1中的所有元素添加到当前的集合中
        Collection col1 = new ArrayList();
        col1.add("456");
        col1.add(789);
        col.addAll(col1);
        System.out.println(col.size()); // 6
        System.out.println(col); // [AA, BB, 123, Mon Dec 21 19:50:05 CST 2020, 456, 789]

        // clear(),清空集合中的所有元素
        col1.clear();
        boolean isEmpty1 = col1.isEmpty();
        System.out.println(isEmpty1); // true

        // isEmpty():判断当前集合是否为空,return size == 0
        boolean isEmpty = col.isEmpty();
        System.out.println(isEmpty); // false

        col.add(new String("Tom"));
        col.add(new Person("Tom", 21));

        // contains(Object obj):其源码是使用 equals()进行比较,需要重写 equals()
        boolean isHas = col.contains(new String("Tom"));
        boolean isHas1 = col.contains(new Person("Tom", 21));
        System.out.println(isHas); // true
        System.out.println(isHas1); // true
        // containsAll(Collection col1)
        Collection col11 = Arrays.asList(123, 789);
        System.out.println(col.containsAll(col11)); // true
    }

}

Iterator迭代器接口

集合元素的遍历操作,使用 Iterator(迭代器)接口

  • Iterator 对象成为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素

  • GOF 给迭代器模式的定义:提供一种方法访问一个容器(Collection)对象中各个元素,而又不需要暴露该对象的内部细节;迭代器模式,就是为容器而生;类似于 “公交车上的售票员、飞机上的空姐...”

  • Collection 接口继承了 java.lang.Iterable 接口,该接口有一个 iterator()方法,那么所有实现了 Collection 接口的集合类都有一个 iterator()方法,用以返回一个实现了 Iterator 接口的对象

  • Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力;如果需要创建一个 Iterator 对象,则必须有一个被迭代的集合

  • 集合对象每次调用 iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前

常用方法

  • boolean hasNext():判断是否还有下一个元素

  • e next()

    1. 指针下移
    2. 将下移以后集合位置上的元素返回
  • remove():删除当前元素

注意:

  • remove:如果还未调用 next()或在上一次调用 next()方法之后已经调用了 remove(),再调用 remove()都会报 IllegalStateException异常
package com.atguigu.java2;

import org.junit.Test;

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

/**
 * 集合元素的遍历,使用 Iterator(迭代器)接口
 *
 * @author lv
 * @create 2020-12-22 20:53
 */
public class IteratorTest {
    @Test
    public void test2() {
        // remove()
        Collection c1 = new ArrayList();
        c1.add(456);
        c1.add(789);
        c1.add(123);
        c1.add(new String("Tom"));
        c1.add("Tom");
        c1.add(new Person("Tom", 21));
        System.out.println(c1); // [456, 789, 123, Tom, Tom, Person{name='Tom', age=21}]
        Iterator i1 = c1.iterator();

//        remove():删除集合中指定的元素
        while(i1.hasNext()) {
            Object o = i1.next();
            if("Tom".equals(o)) {
                i1.remove();
            }
            System.out.println(o); // ...
        }
        System.out.println(c1); // [456, 789, 123, Person{name='Tom', age=21}]

    }
    @Test
    public void test1() {
        Collection c1 = new ArrayList();
        c1.add(456);
        c1.add(789);
        c1.add(123);
        c1.add(new Person("Tom", 21));


        Iterator i1 = c1.iterator();

        // 方式一:
//        System.out.println(i1.next()); // 456
//        System.out.println(i1.next()); // 789
//        System.out.println(i1.next()); // 123
//        System.out.println(i1.next()); // Person{name='Tom', age=21}
        // 报异常:java.util.NoSuchElementException
//        System.out.println(i1.next());

//        方式二:
//        for (int i = 0; i < c1.size(); i++) {
//            System.out.println(i1.next());
//        }

//        方式三:推荐写法
        while(i1.hasNext()) {
            System.out.println(i1.next());
        }
    }
}

使用 forEach 遍历集合、数组

  • Java5.0提供了 增强for循环迭代访问 Collection和数组
  • 该遍历操作不需要获取 Collection或数组的长度、无需使用索引访问元素
  • 遍历集合的底层调用 Iterator完成操作
  • forEach 还可以用来遍历数组
// for(元素类型 局部变量 : 集合/数组) { // ... }
package com.atguigu.java2;

import org.junit.Test;

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

/**
 * for:
 * 
 * @author lv
 * @create 2020-12-23 19:48
 */
public class ForTest {
    @Test
    // 笔试题
    public void test3() {
        int[] arrs = new int[]{1, 2, 8, 9};
//        方式一:
//        for (int i = 0; i < arrs.length; i++) {
//            arrs[i] = 5;
//        }
//        方式二:
        for (int o : arrs) {
            o = 6;
        }
//
        for (int i = 0; i < arrs.length; i++) {
            System.out.println(arrs[i]);
            // 方式一:5 ...
            // 方式二:1, 2, 8, 9
        }
    }
    @Test
    public void test2() {
        String[] strArr = new String[]{"123", "456", "sfgvasfv"};
        for(String o : strArr) {
            System.out.println(o); // ...
        }

    }
    @Test
    public void test() {
        Collection c1 = new ArrayList();
        c1.add(456);
        c1.add(789);
        c1.add(123);
        c1.add(new Person("Tom", 21));
        c1.add(true);
        for(Object obj : c1) {
            System.out.println(obj); // ...
        }
    }
}

Collection子接口一:List*****

List接口(动态数组)概述:

  • 鉴于Java中数组用来存储数据的局限性,通常使用List替代数组
  • List集合类中 元素有序、且可重复,集合中的每个元素都有其对应的顺序索引
  • List容器中的元素对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素
  • JDK API中 List接口的 实现类 常用的有:ArrayList、LinkedList 和 Vector

ArrayList、LinkedList、Vector 三者的异同

  • 同:
    1. 三个类都实现了 list接口,存储数据的特点相同,有序可重复的数据
  • 异:
    1. ArrayList:作为 List接口的主要实现类;线程不安全的,效率高;底层使用 Object[] elementData存储
    2. LinkedList:对于频繁的插入、删除操作,使用此类比ArrayList效率高;底层使用双向链表存储
    3. Vector:作为 List接口的古老实现类(JDK 1.0);线程安全的,效率低;底层使用Object[] elementData存储

ArrayList 源码分析:

JDK 1.7中(饿汉式)

  • ArrayList list = new ArrayList(); // 底层创建了长度为 10的 Object[]数组 elementData

  • list.add(123); // elementData[0] = new Integer(123); ... list.add(11); // 如果此次添加导致底层 elementData数组容量不够,则扩容 默认情况下扩容为原来容量的 1.5倍,并且将原有数组中的数据 复制到新的数组中

  • 结论:建议开发中使用代参的构造器,ArrayList list = new ArrayList(int capacity)

JDK 1.8中(懒汉式)

  • ArrayList list = new ArrayList(); // 底层 Object[] elementData初始化为{},并没有创建长度为 10的数组,当第一次调用 add()时,list.add(123) 底层才创建了长度为 10的数组,并将数据123添加到 elementData数组中;后续的添加和扩容操作与 JDK1.7 无异

小结:

  • JDK1.7中的ArrayList的对象的创建类似于单例的 饿汉式,而JDK1.8中的ArrayList的对象的创建类似于单例的 懒汉式,延迟了数组的创建,节省内存

LinkedList(链表) 源码分析:

JDK1.8中

  • LinkedList list = new LinkedList(); 内部声明了 Node类型的first和last属性,默认值为 null;list.add(123);// 将123封装到 Node中,创建了 Node对象,其中 Node定义为:(如下部分源码,体现了双向链表的说法)
    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;
        }
    }

Vector 源码分析:

  • JDK1.7和JDK1.8中通过 Vector()构造器创建对象时,底层都创建了长度为 10的数组,在扩容方面,默认扩容为原来数组长度的二倍

List 接口方法

List除了从Collection集合继承的方法外,List集合里添加了一些根据索引来操作集合元素的方法

  • void add(int index, Object ele):在index位置插入 ele元素

  • void addAll(int index, Collection c):从index开始,将c中的所有元素添加进来

  • Object get(int index):获取指定位置的元素

  • int indexOf(Object o):返回 o在集合中首次出现的位置index,未找到返回 -1

  • int lastIndexOf(Object o):返回 o在集合中末次出现的位置

  • Object remove(int index):(重载)移除指定 index位置的元素,并返回此元素

  • set(int index, Object o):替换指定 index位置的元素为 ele,并返回被替换的元素

  • List subList(int fromIndex, int toIndex):返回从 fromIndex到toIndex位置之前的子集合 [fromIndex, toIndex)

package com.atguigu.java;

import org.junit.Test;

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

/**
 * List
 *
 * ArrayList 源码分析 JDK 1.7
 *
 * @author lv
 * @create 2020-12-23 20:41
 */
public class ListTest {
    @Test
    public void test2 () {

        // int indexOf(Object o):返回 o在集合中首次出现的位置index,未找到返回 -1
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(987);
        list.add("123");
        int index = list.indexOf(987);
        System.out.println(index + " :" + list); // 2 :[123, 456, 987, 123]
        // int lastIndexOf(Object o):返回 o在集合中末次出现的位置
        int lastIndex = list.lastIndexOf(456);
        System.out.println("456: " + lastIndex); // 456: 1

        // Object remove(int index):移除指定 index位置的元素,并返回此元素
        Object o1 = list.remove(lastIndex);
        System.out.println(o1 + " : " + list); // 456 : [123, 987, 123]

        // set(int index, Object o):替换指定 index位置的元素为 ele,并返回被替换的元素
        Object l1 = list.set(2, "GGG");
        System.out.println(l1 + " : " + list); // 123 : [123, 987, GGG]

        // List subList(int fromIndex, int toIndex):返回从 fromIndex到toIndex位置之前的子集合 [fromIndex, toIndex)
        List l2 = list.subList(1, list.size());
        System.out.println(l2 + " : " + list); // [987, GGG] : [123, 987, GGG]
    }
//    LinkedList list = new LinkedList();
    @Test
    public void test () {
        ArrayList l1 = new ArrayList();
        l1.add("aaa");
        l1.add("SSS");
        ArrayList list = new ArrayList();
        list.add(123);
        list.add(456);
        list.add(987);
        list.add("123");
        System.out.println(list); // [123, 456, 987, 123]
        // add(int index, Object obj)插入操作
        list.add(1, "cc");
        list.add(2, "DD");
        System.out.println(list); // [123, cc, DD, 456, 987, 123]
        // addAll(int index, Collection c)
        list.addAll(2, l1);
        System.out.println(list); // [123, cc, aaa, SSS, DD, 456, 987, 123]
        List l2 = Arrays.asList("DDD", "rrr");
        list.addAll(2, l2);
        System.out.println(list); // [123, cc, DDD, rrr, aaa, SSS, DD, 456, 987, 123]

        // get(int index):获取指定位置的元素
        Object o = list.get(1);
        System.out.println(o); // cc
    }
}

总结:常用方法

  • 增:add(Object o)
  • 删:remove(int index)/remove(Object o)
  • 改:set(int index, Object o)
  • 查:get(int index)
  • 插:add(int index, Object o)
  • 长度:size()
  • 遍历:
    1. Iterator迭代器方式
    2. 增强for(Object o : list)
    3. 普通for循环
     @Test
     public void test3 () {
         ArrayList list = new ArrayList();
         list.add(123);
         list.add(456);
         list.add(987);
         list.add("123");
         Iterator i1 = list.iterator();
         while (i1.hasNext()) {
             System.out.println(i1.next());
         }
         System.out.println("///////////////////////");
         for (Object o : list) {
             System.out.println(o);
         }
         System.out.println("///////////////////////");
         for (int i = 0; i < list.size(); i++) {
             System.out.println(list.get(i));
         }
     }
    

Collection子接口二:Set

Set接口概述:

  • Set接口是Collection的子接口,set接口没有提供额外的方法
  • Set集合不允许包含相同的元素,如果将两个相同的元素加入Set集合中,则添加操作失败
  • Set判断两个对象是否相同,不是使用 == 运算符,而是根据 equals() 方法
  • 存储无序的、不可重复的数据

要求:

  1. 向Set中添加的数据,其所在的类一定要重写 hashCode()和equals()
  2. 重写的hashCode()和equals()尽可能保持一致性:相同的对象必须具有相同的散列码(哈希值)

数据存储说明:

 * 一、Set:存储无序的、不可重复的数据
 * 1.无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,
 * 而是根据被添加数据的 hashCode哈希值决定的,
 *
 * 2.不可重复性:保证添加的元素按照 equals()判断时,不能返回true;即:相同的元素只能添加一个
 *
 *
 * 二、添加元素的过程:以 HashSet为例
 * 我们向 HashSet中添加元素 a,首先调用元素 a所在类的 hashCode(),计算元素a的哈希值,
 * 此哈希值接着通过某种算法计算出 HsahSet底层数组中的存放位置(索引位置),判断
 * 数组此位置上是否已经有元素:
 *  如果此位置上没有其它元素,则元素a添加成功 -> 情况1
 *  如果此位置上有其它元素b(或以链表形式存在多个元素),则比较元素a和元素b的哈希值:
 *      如果哈希值不同,则元素a添加成功;-> 情况2
 *      如果哈希值相同,进而需要调用元素a所在类的 equals()方法:
 *          equals()返回值为true,则添加失败
 *          equals()返回值为false,则添加成功 -> 情况3
 *
 *      对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储
 *          jdk1.7:元素a放在数组中,指向原来的元素
 *          jdk1.8:原来的元素在数组中,指向元素a
 *          总结:七上八下

Set实现类 HashSet

  • 数据底层存储方式为:数组 + 链表
  • HashSet是Set接口的典型实现,大多数时候使用Set集合时都是用这个实现类
  • HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能
  • HashSet具有以下特点
    1. 不能保证元素的排列顺序
    2. HashSet是线程不安全的
    3. 集合中的元素可以是 null
  • HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的 equals() 方法返回值相等
  • 对于存放在Set容器中的对象,对应的类一定要重写 equals()和hashCode(Object o)方法,以实现对象相等规则;即:相等的对象必须具有相等的散列码
package com.atguigu.java1;

import org.junit.Test;

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

/**
 * @author lv
 * @create 2020-12-27 16:51
 */
public class SetTest {
    /**
     * 一、Set:存储无序的、不可重复的数据
     *
     * 1.无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,
     * 而是根据被添加数据的 hashCode哈希值决定的,
     *
     * 2.不可重复性:保证添加的元素按照 equals()判断时,不能返回true;即:相同的元素只能添加一个
     *
     *
     * 二、添加元素的过程:以 HashSet为例
     * 我们向 HashSet中添加元素 a,首先调用元素 a所在类的 hashCode(),计算元素a的哈希值,
     * 此哈希值接着通过某种算法计算出 HsahSet底层数组中的存放位置(索引位置),判断
     * 数组此位置上是否已经有元素:
     *  如果此位置上没有其它元素,则元素a添加成功 -> 情况1
     *  如果此位置上有其它元素b(或以链表形式存在多个元素),则比较元素a和元素b的哈希值:
     *      如果哈希值不同,则元素a添加成功;-> 情况2
     *      如果哈希值相同,进而需要调用元素a所在类的 equals()方法:
     *          equals()返回值为true,则添加失败
     *          equals()返回值为false,则添加成功 -> 情况3
     *
     *      对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上的数据以链表的方式存储
     *          jdk1.7:元素a放在数组中,指向原来的元素
     *          jdk1.8:原来的元素在数组中,指向元素a
     *          总结:七上八下
     *
     */
    @Test
    public void test () {
        Set s1 = new HashSet();
        s1.add(456);
        s1.add(123);
        s1.add("AA");
        s1.add("CC");
        s1.add(new UserTest("Tom", 12));
        s1.add(new UserTest("Tom", 12));
        System.out.println(s1);

        Iterator i1 = s1.iterator();
        while (i1.hasNext()) {
            System.out.println(i1.next());
        }
    }
}
// *********************************
package com.atguigu.java1;

/**
 * @author lv
 * @create 2020-12-27 20:32
 */
public class UserTest {
    private String name;
    private int age;

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

    @Override
    public boolean equals(Object o) {
        System.out.println("User equals ... ");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        UserTest userTest = (UserTest) o;

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

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

Set实现类 LinkedHashSet

  • LinkedHashSet是 HashSet的子类
  • LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的
  • LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能
  • LinkedHashSet不允许集合元素重复

优点:

  • 对于频繁的遍历操作,LinkedHashSet效率高于 HashSet
    /**
     * LinkedHashSet 的使用
     * LinkedHashSet 作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,
     * 记录此数据前一个和后一个数据的位置
     *
     * 优点:对于频繁的遍历操作,LinkedHashSet效率高于 HashSet
     */
    @Test
    public void test2 () {
        Set s1 = new LinkedHashSet();
        s1.add(456);
        s1.add(123);
        s1.add("AA");
        s1.add("CC");
        s1.add(new UserTest("Tom", 12));
        s1.add(new UserTest("Tom", 12));
        System.out.println(s1); // [456, 123, AA, CC, UserTest{name='Tom', age=12}]
    }

Set实现类 TreeSet

  • TreeSet是SortedSet接口的实现类,TreeSet可以确保集合元素处于排序状态
  • 可以按照添加对象的指定属性,进行排序
  • TreeSet底层使用红黑树结构存储数据,后边讲的TreeMap也是如此
  • 新增的方法如下:
    1. Comparator comparator()
    2. Object first()
    3. Object last()
    4. Object lower(Object o)
    5. Object higher(Object o)
    6. SortedSet subSet(fromElement, toElement)
    7. SortedSet headSet(toElement)
    8. SortedSet tailSet(fromElement)
  • TreeSet 两种排序方法:自然排序定制排序;默认情况下,TreeSet采用自然排序

特点:

  • 有序,查询速度比 List快
  • 数据存放方式为:小左右大
  • 在自然排序中,比较两个对象是否相同的标准:compareTo()返回0,不再使用equals()进行判断
  • 在定制排序中,比较两个对象是否相同的标准:compare(o1, o2)返回0,不再使用equals()进行判断
package com.atguigu.java1;

import org.junit.Test;

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

/**
 * @author lv
 * @create 2020-12-28 20:14
 */
public class TreeSetTest {
    @Test
    public void test2 () {
//        TreeSet t1 = new TreeSet(new Comparator() {
//            @Override
//            public int compare(Object o1, Object o2) {
//                return 0;
//            }
//        });

        Comparator comparator = new Comparator() {
//            按照年龄从小到大排序
            @Override
            public int compare(Object o1, Object o2) {
//                return 0;
                if (o1 instanceof UserTest && o2 instanceof UserTest) {
                    UserTest u1 = (UserTest) o1;
                    UserTest u2 = (UserTest) o2;
                    return Integer.compare(u1.getAge(),  u2.getAge());
                } else {
                    throw new RuntimeException("输入数据类型有误");
                }
            }
        };
        TreeSet ts1 = new TreeSet(comparator);
        ts1.add(new UserTest("Tom", 12));
        ts1.add(new UserTest("Jiery", 18));
        ts1.add(new UserTest("Mike", 12));
        ts1.add(new UserTest("Lory", 20));
        ts1.add(new UserTest("Lory", 18));

        Iterator iterator = ts1.iterator();

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

    }
    /**
     * TreeSet:
     *
     * 1.向 TreeSet中添加的数据,要求是相同类的对象
     *
     * 2.两种排序方式:
     *
     *  自然排序:compareTo()
     *
     *  定制排序:comparator()
     *
     *  在自然排序中,比较两个对象是否相同的标准:compareTo()返回0,不再使用equals()进行判断
     *
     *  在定制排序中,比较两个对象是否相同的标准:compare(o1, o2)返回0,不再使用equals()进行判断
     *
     *
     *
     */
    @Test
    public void test () {
//        Set treeSet = new TreeSet();
        TreeSet ts = new TreeSet();

        // error:不能添加不同类的对象
//        ts.add(123);
//        ts.add(456);
//        ts.add("AA");
//        ts.add(new UserTest("Tom", 12));
//        System.out.println(ts);

        // 1.添加整型数据
        ts.add(123);
        ts.add(456);
        ts.add(789);
        ts.add(-126);
        ts.add(-199);
        System.out.println(ts); // [-199, -126, 123, 456, 789]

        Iterator i1 = ts.iterator();

        while (i1.hasNext()) {
            System.out.println(i1.next()); // 默认按照 从小到大排序
        }

        TreeSet ts1 = new TreeSet();

        ts1.add(new UserTest("Tom", 12));
        ts1.add(new UserTest("Jiery", 18));
        ts1.add(new UserTest("Mike", 19));
        ts1.add(new UserTest("Lory", 20));
        ts1.add(new UserTest("Lory", 50));
        // 按照比较的结果判断两个元素是否为同一个元素,compareTo返回 true认为是同一个元素
        System.out.println(ts1);

        Iterator i2 = ts1.iterator();
        while (i2.hasNext()) {
            System.out.println(i2.next());
        }
//         [UserTest{name='Jiery', age=18}, UserTest{name='Lory', age=20}, UserTest{name='Mike', age=19}, UserTest{name='Tom', age=12}]
    }
}

Map接口*****

实现类结构树

  • Hashtable <- Properties
  • HashMap <- LinkedHashMap
  • SortedMap <- TreeMap

Map:双列数据,用于存储 key-value对的数据 -- 类似于高中的函数:y = f(x);

Map结构的理解

  • Map中的 key:无序的、不可重复的,使用 Set存储所有的 key --> 要求 key所在的类要重写 equals()和hashCode()方法
  • Map中的 value:无序的、可重复的,使用 Collection存储所有的 value --> value所在的类要重写 equals()方法
  • 一个键值对:key-value 构成一个 Entry(key, value)对象
  • Map中的 entry:无序的、不可重复的,使用 Set存储所有的 entry

Map接口中定义的方法

添加、删除、修改操作

  • Object put(Object key, Object value):将指定的key-value添加(修改)到当前map对象中
  • void putAll(Map m):将 m 中的所有key-value对存放到当前map中
  • Object remove(Object key):移除指定key的key-value对,并返回value
  • void clear():清空当前map中的所有数据

元素查询操作

  • Object get(Object key):获取指定key对应的value
  • boolean containsKey(Object value):是否包含指定的key
  • boolean containsValue(Object value):是否包含指定的value
  • int size():返回map中key-value对的个数
  • boolean isEmpty():判断当前map是否为空
  • boolean equals(Object obj):判断当前map和参数对象obj是否相等

元视图操作的方法

  • Set keySet():返回所有key构成的Set集合
  • Collection value():返回所有value构成的Collection集合
  • Set entrySet():返回所有key-value对构成的Set集合

总结:常用方法

  • 增:put(Object key, Object value)、putAll(Map m)
  • 删:remove(Object key)
  • 改:put(Object key, Object value)
  • 查:get(Object key)、containsKey(Object key)、containsValue(Object value)
  • 长度:size()
  • 遍历:keySet()、values()、entrySet()

示例

package com.atguigu.java;

import org.junit.Test;

import java.util.*;

/**
 * @author lv
 * @create 2021-01-02 15:29
 */
public class MapTest {
    @Test
    public void test3 () {
        HashMap h1 = new HashMap();
        h1.put("name", "Tom1");
        h1.put("name", "Tom");
        h1.put("age", 23);
        h1.put("address", "罗xxx123栋");
        h1.put("Tel", 15603266088L);
        System.out.println(h1);
        // {address=罗xxx123栋, name=Tom, Tel=15603266088, age=23}

//        keySet() 返回所有 key组成的 Set集合
        Set s1 = h1.keySet();
        for (Object o : s1) {
            System.out.println("key: " + o); // key: address ...
        }
        Iterator i1 = s1.iterator();
        while (i1.hasNext()) {
            System.out.println(h1.get(i1.next())); // HashMap遍历
        }

//        values() 返回所有 value的 Collection集合
        Collection v1 = h1.values();
        for (Object o : v1) {
            System.out.println("value: " + o); // value: 罗xxx123栋
        }
        Iterator i2 = v1.iterator();
        while (i2.hasNext()) {
            System.out.println(i2.next()); // values遍历
        }

//        entrySet() 返回所有 key-value 的 Set集合
        Set s2 = h1.entrySet();
        Iterator i3 = s2.iterator();
//        方式一:
        while (i3.hasNext()) {
//            System.out.println(i3.next()); // address=罗xxx123栋 ...
            Object n1 = i3.next();
            Map.Entry e1 = (Map.Entry) n1;
            System.out.println(e1.getKey() + ": " + e1.getValue());
            // address: 罗xxx123栋 ...
        }
        Set s11 = h1.keySet();
        for (Object key : s11) {
            System.out.println(key + " :for: " + h1.get(key));
            // address :for: 罗xxx123栋 ...
        }
        Iterator i11 = s11.iterator();
//        方式二:
        while (i11.hasNext()) {
            Object key = i11.next();
            Object value = h1.get(key);
            System.out.println(key + ":" + value);
            // address:罗xxx123栋 ...
        }

    }

    @Test
    public void test2 () {
        HashMap h1 = new HashMap();

//        put() 添加、修改
        h1.put("name", "Tom1");
        h1.put("name", "Tom");
        h1.put("age", 23);
        HashMap h2 = new HashMap();
        h2.put("address", "罗xxx123栋");
        h2.put("Tel", 15603266088L);
        h1.putAll(h2);
        System.out.println(h1); // {address=罗xxx123栋, name=Tom, Tel=15603266088, age=23}

//        remove(key) 移除
        Object o1 = h1.remove("Tel");
        System.out.println(o1); // 15603266088
        System.out.println(h1); // {address=罗xxx123栋, name=Tom, age=23}

//        clear() 清空
        h2.clear();
        System.out.println(h2); // {}
        System.out.println(h1); // {address=罗xxx123栋, name=Tom, age=23}

//        size() 键值对个数
        int size = h1.size();
        System.out.println(size); // 3

//        get(Object key) 获取指定key的value
        Object o2 = h1.get("name");
        System.out.println(o2); // Tom

//        containsKey(Object key) 是否包含指定的key
        boolean isHave = h1.containsKey("age");
        System.out.println(isHave); // true

//        containsValue
        boolean isValue = h1.containsValue("Tom");
        System.out.println(isValue); // true

//        isEmpty
        boolean isEmpty = h1.isEmpty();
        System.out.println(isEmpty); // false
        isEmpty = h2.isEmpty();
        System.out.println(isEmpty); // true

//        equals(Object o)
        boolean isEquals = h1.equals(h2);
        System.out.println(isEquals); // false

    }
    @Test
    public void test1 () {
        LinkedHashMap l1 = new LinkedHashMap();
        l1.put("name", "tom");
        l1.put("age", 16);
        l1.put("address", "西xxx区18栋");
        System.out.println(l1); // {name=tom, age=16, address=西xxx区18栋}
    }
    @Test
    public void test () {
        Map map = new HashMap();
        map.put("name", "lv");
        map.put(null, 123);
        map.put("age", 18);
        System.out.println(map); // {null=123, name=lv, age=18}
    }
}

HashMap

作为 Map的主要实现类,线程不安全的,效率高;可以存储 null的key和value

HashMap的底层:

  • 数组 + 链表(jdk7及之前)
  • 数组 + 链表 + 红黑树(jdk8)

HashMap底层实现原理:

jdk1.7 底层数据存储原理:

  • HashMap map = new HashMap(); 在实例化后,底层创建了长度为 16的一维数组 Entry[] table

  • ...多次put后..map.put(key1, value1); 首先,调用 key1所在类的 hashCode()计算 key1哈希值,此哈希值经过某种算法计算后,得到在 Entry数组中的存放位置,如果此位置上的数据为空,此时的 Entry(key1, value1)添加成功 --> 情况1

  • 如果此位置上的数据不为空(意味着此位置上存在一个或多个数据),比较 key1和已经存在的一个或多个数据的 哈希值:

    1. 如果 key1的哈希值与已经存在的数据的哈希值不同,此时 Entry(key, value)添加成功 --> 情况2

    2. 如果 key1的哈希值和已经存在的某一个数据的哈希值相同,继续比较,调用 key1所在类的 equals()方法,比较:

      • 如果 equals()返回 false,此时 Entry(key1, value1)添加成功 --> 情况3
      • 如果 equals()返回 true,使用 value1替换相同 key的value值
  • 补充:关于 情况2、情况3 此时 key1-value1和原来的数据已链表的方式存储

  • 在不断的添加过程中,会涉及到扩容问题,但超出临界值threshold(且当前要存放位置非空),默认扩容方式:扩容为原来容量的2倍,并将原有数据复制过来

jdk1.8 底层数据存储原理与 jdk1.7不同点:

  1. new HashMap():底层创建的数组长度为 0
  2. jdk1.8 底层的数组是,Node[],而非 Entry[]
  3. 首次调用 put()方法时,底层创建长度为 16的数组
  4. jdk1.7底层结构:数组 + 链表;jdk1.8底层结构:数组 + 链表 + 红黑树;当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64 时,此时此索引位置上的所有数据改为使用 红黑树

HashMap 源码中的重要常量

  • DEFAULT_INITIAL_CAPACITY:HashMap的默认容量 16
  • MAXIMUM_CAPACITY:HashMap的最大支持容量 2^30
  • DEFAULT_LOAD_FACTOR:默认加载因子 0.75
  • TREEIFY_THRESHOLD:Bucket中链表长度大于该默认值,转化为红黑树 8
  • UNTREEIFY_THRESHOLD:Bucket中红黑树存储的Node小于该默认值,转化为链表
  • MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量 64
  • table:存储元素的数组,总是2的n次幂
  • entrySet:存储具体元素的集
  • size:HashMap中存储的键值对的数量
  • modCount:HashMap扩容和结构改变的次数
  • threshold:扩容的临界值,= 容量 * 填充因子 16 * 0.75 = 12
  • loadFactor:填充因子

LinkedHashMap(HashMap的子类)

保证在遍历 map元素时,可以按照添加的顺序实现遍历;原因:在原有的 HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素;对于频繁的遍历操作,此类执行效率高于 HashMap

LinkedHashMap的底层实现原理

package com.atguigu.java;

import org.junit.Test;

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

/**
 * @author lv
 * @create 2021-01-02 15:29
 */
public class MapTest {
    @Test
    public void test1 () {
        LinkedHashMap l1 = new LinkedHashMap();
        l1.put("name", "tom");
        l1.put("age", 16);
        l1.put("address", "西xxx区18栋");
        System.out.println(l1); // {name=tom, age=16, address=西xxx区18栋}
    }
    @Test
    public void test () {
        Map map = new HashMap();
        map.put("name", "lv");
        map.put(null, 123);
        map.put("age", 18);
        System.out.println(map); // {null=123, name=lv, age=18}
    }
}

TreeMap

保证按照添加的 key-value对进行排序,实现排序遍历;此时考虑 key的自然排序或定制排序;底层使用的是 红黑树存储数据

package com.atguigu.java;

/**
 * @author lv
 * @create 2021-01-06 19:51
 */

public class User implements Comparable {

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

    @Override
    public boolean equals(Object o) {
        System.out.println("User equals ... ");
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User userTest = (User) o;

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

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

//        按照姓名 从小到大排列
    @Override
    public int compareTo(Object o) {
//        return 0;
        if (o instanceof User) {
            User user = (User) o;
            int compare = this.name.compareTo(user.name);
            if (compare != 0) {
                return compare;
            } else {
                return Integer.compare(this.age, user.age);
            }
//            return this.name.compareTo(user.name);
        } else {
            throw new RuntimeException("输入的类型不匹配");
        }
    }
}

// ***********************

package com.atguigu.java;

import org.junit.Test;

import java.util.Comparator;
import java.util.Set;
import java.util.TreeMap;

/**
 *  Map
 *  TreeMap 按照 key 进行排序:自然排序、定制排序
 *
 * @author lv
 * @create 2021-01-06 19:44
 */
public class TreeMapTest {
    @Test
//    定制排序
    public void test1 () {
        Comparator c1 = new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
//                if (o1 instanceof Map.Entry && o2 instanceof Map.Entry) {
//                    Map.Entry e1 = (Map.Entry)o1;
//                    Map.Entry e2 = (Map.Entry)o2;
////                    Object key = e1.getKey();
//                    Object value1 = e1.getValue();
//                    Object value2 = e1.getValue();
//                    value1.
//                    return Integer.compare(value1, value2);
//                }
                if (o1 instanceof User && o2 instanceof User) {
                    User u1 = (User)o1;
                    User u2 = (User)o2;

                    return Integer.compare(u1.getAge(), u2.getAge());
                }
                throw new RuntimeException("输入的数据格式错误!");
//                return 0;
            }

            @Override
            public boolean equals(Object obj) {
                return false;
            }
        };
        TreeMap t1 = new TreeMap(c1);
        User u1 = new User("Tom", 24);
        User u2 = new User("Jerry", 26);
        User u3 = new User("Jack", 20);
        User u4 = new User("Rose", 21);

        t1.put(u1, 98);
        t1.put(u2, 120);
        t1.put(u3, 102);
        t1.put(u4, 118);
        Set s1 = t1.keySet();
        for (Object o : s1) {
            System.out.println(o + " :" + t1.get(o));
        }

    }
    @Test
//    自然排序
    public void test () {
        TreeMap t1 = new TreeMap();

        User u1 = new User("Tom", 24);
        User u2 = new User("Jerry", 26);
        User u3 = new User("Jack", 20);
        User u4 = new User("Rose", 21);

        t1.put(u1, 98);
        t1.put(u2, 120);
        t1.put(u3, 102);
        t1.put(u4, 118);

		// 方式一:
        Set s1 = t1.keySet();
        for (Object o : s1) {
            System.out.println("key: " + o + " value: " + t1.get(o));
        }
        
        // 方式二:
        Set es = t1.entrySet();
        Iterator i1 = es.iterator();
        while (i1.hasNext()) {
            Object next = i1.next();
            Map.Entry e = (Map.Entry)next;
            System.out.println(e.getKey() + " :" + e.getValue());
        }
    }
}

Hashtable

作为古老的实现类,线程安全的,效率低;不能存储 null的key和value

Properties(Hashtable的子类)

该对象用于处理配置文件;key和 value都是String类型

package com.atguigu.java;

import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/**
 *
 * Propertise 常用于处理配置文件
 * @author lv
 * @create 2021-01-07 19:17
 */
public class PropertiseTest {
    public static void main(String[] args) {

        FileInputStream fis = null;
        try {
            Properties pros = new Properties();
            fis = new FileInputStream("jdbc.properties");
            pros.load(fis);
            String name = pros.getProperty("name");
            String password = pros.getProperty("password");
            System.out.println("name: " + name + ", password: " + password);
            // name: Tom, password: abc123
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
// *********************

// jdbc.properties 配置文件
name=Tom
password=abc123

Collections工具类

  • Collections 是操作 Set、List 和 Map 等集合的工具类;Arrays 是操作数组的工具类
  • Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法

方法:

排序操作:(均为 static 方法)

  • reverse(List):反转 List中的元素顺序
  • shuffle(List):对 List集合中的元素进行随机排序
  • sort(List):根据元素的自然顺序对指定 List集合元素按升序排序
  • sort(List, Comparator):根据指定的 Comparator 产生的顺序对 List集合元素进行排序
  • swap(List, int i, int j):将指定 list集合中的 i 处元素和 j 处元素进行交换

查找、替换:

  • Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
  • Object max(Collection, Comparator):根据 Comparator 指定的顺序,给定集合中的最大元素
  • Object min(Collection):
  • Object min(Collection, Comparator):
  • int frequency(Collection, Object):返回指定集合中指定元素的出现次数
  • void copy(List dest, List src):将 src中的内容复制到 dest中
  • boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换 List对象的所有旧值

同步控制:

Collections 类中提供了多个 synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而解决多线程并发访问集合时的线程安全问题

package com.atguigu.java;

import org.junit.Test;

import java.util.*;

/**
 * Collections 是操作 Collection和 Map的工具类
 *
 *
 * @author lv
 * @create 2021-01-07 20:16
 */
public class CollectionsTest {

    /**
     * Collections 类中提供了多个 `synchronizedXxx()`方法,
     * 该方法可使将指定集合包装成线程同步的集合,
     * 从而解决多线程并发访问集合时的线程安全问题
     */
    @Test
    public void test1 () {
        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
        List list1 = Collections.synchronizedList(list);
//        此时返回的 list1 就是线程安全的
    }

    @Test
    public void test () {

        List list = new ArrayList();
        list.add(123);
        list.add(43);
        list.add(765);
        list.add(-97);
        list.add(0);
//        Collections.reverse(list);
//        System.out.println(list); // [0, -97, 765, 43, 123]
//        Collections.shuffle(list);
//        System.out.println(list); // [43, 123, 0, -97, 765]
//        Collections.sort(list);
//        System.out.println(list); // [-97, 0, 43, 123, 765]
        Collections.sort(list, new Comparator() {
            @Override
            public int compare(Object o1, Object o2) {
                if (o1 instanceof Integer && o2 instanceof Integer) {
                    Integer n1 = (Integer)o1;
                    Integer n2 = (Integer)o2;
                    return -Integer.compare(n1, n2);
                }
                throw new RuntimeException("输入数据类型有误");
//                return 0;
            }
        });
        System.out.println(list); // [765, 123, 43, 0, -97]

        Collections.swap(list, 1, 3);
        System.out.println(list); // [765, 0, 43, 123, -97]

        int sum = Collections.frequency(list, 0);
        System.out.println(sum); // 1

//        ArrayList arrayList = new ArrayList();
//        arrayList.add(012);
//        arrayList.add(013);
//        arrayList.add(014);
//        arrayList.add(015);

//        技巧
        List dest = Arrays.asList(new Object[list.size()]);
        System.out.println(dest.size()); // 5
        System.out.println(dest); // [null, null, null, null, null]

        Collections.copy(dest, list);
        System.out.println(dest);

    }
}

问题

比较 throw 和 throws

上游排污,下游治污

  • throws:一种处理异常的方式;使用在方法声明的末尾,将异常抛给上级

  • throw:生成一个异常对象,并抛出;使用在方法内部(一般自动抛出)

谈谈你对同步代码块中同步监视器和共享数据的理解及各自的要求

  • 同步监视器:任何一个类的对象都可以充当锁、多个线程共用同一把锁
  • 共享数据:多个线程共同操作的数据,即为共享数据;需要使用同步机制将共享数据包裹起来;不能包多也不能包少

集合 Collection 中存储的如果是自定义类的对象,需要自定义类重写那些方法

  • equals()方法:调用Collection中的一些方法时内部需要使用 equals()进行判断;如:contains()、remove()、retainsAll()...

  • List:equals()方法

  • Set:

    1. HashSet、LinkedHashSet:equals()、hashCode()
    2. TreeSet:(Comparable)compareTo(Object o)、(Comparator)compare(Object o1, Object o2)

ArrayList、LinkedList、Vector三者的异同点

相同点:

  • 都是 List接口的实现类,存储的数据都是有序、可重复的

不同点:

  • Vector线程时安全的,程序效率低
  • ArrayList、LinkedList是线程不安全的,程序效率高
  • ArrayList、Vector 都使用数组存储数据
  • LinkedList 使用链表存储数据
  • 在扩容时ArrayList是1.5倍,Vector是2倍
  • 在插入和删除元素方面,LinkedList的效率要高于ArrayList
  • 在查找元素方面ArrayList和效率要高于LinkedList

List接口的常用方法

  • 增:add(Object o)
  • 删:remove(Object o)/remove(int index)
  • 改:set(int index, Object o)
  • 查:get(int index)
  • 插:add(int index, Object o)
  • 长度:size()
  • 遍历:
    1. Iterator迭代器
    2. 增强 for循环
    3. for 循环

Set存储数据的特点,常见的实现类有那些,特点是...

存储数据的特点:

  • 无序的、不重复的

常用的实现类:

  • HashSet
  • LinkedHashSet
  • TreeSet

在List内去除重复数字值,要求尽量简单

    @Test
    public void test1 () {
        List l1 = new ArrayList();
        l1.add(new Integer(1));
        l1.add(new Integer(1));
        l1.add(new Integer(2));
        l1.add(new Integer(3));
        l1.add(new Integer(2));
        l1.add(new Integer(3));
        List l2 = duplicateList(l1);

        for (Object o : l2) {
            System.out.println(o);
        }
    }
    public List duplicateList (List list) {
        HashSet hashSet = new HashSet();
        hashSet.addAll(list);
        return new ArrayList(hashSet);
    }

Map 存储数据的特点是?并指明 key、value、entry存储数据的特点

Map:

  • 双列数据,存储 key-value 对数据

key:

  • 无序的、不可重复的 -> Set存储

value:

  • 无序的、可重复的 -> Collection

entry:

  • key-value对、无序的、不可重复的 -> Set存储

谈谈你对HashMap中 put、get方法的认识

HashMap的扩容机制,默认大小是多少

HashMap中什么是负载因子(填充比)

  • 负载因子的大小决定了 HashMap的数据密度
  • 负载因子越大密度越大,发生碰撞的几率越高,数组中的链表越长,造成查询或插入时的比较次数增多,性能会下降
  • 负载因子越小,就越容易出发扩容,数据密度也越小,意味着发生碰撞的几率越小,数组中的链表也越短,查询和插入时比较的次数也越小,性能会更高;但会浪费一定的内容空间,而且经常扩容也会影响性能,建议初始化预设大一点的空间

HashMap中什么是吞吐临界值(阈值、threshold)

HashMap的底层实现原理

  • 数组 + 链表 + 红黑树

LinkedHashMap 的底层实现原理

LinkedHashMap底层使用的结构与HashMap相同,因为 LinkedHashMap继承于 HashMap;区别在于:LinkedHashMap内部提供了 Entry,替换 HashMap中的 Node,Entry中多出两个变量:Entry before, after

HashMap和Hashtable的异同

CurrentHashMap与Hashtable的异同

笔试题

    @Test
    public void test3 () {
        HashSet h1 = new HashSet();
        Person p1 = new Person(1001, "AA");
        Person p2 = new Person(1002, "BB");

        h1.add(p1);
        h1.add(p2);

        System.out.println(h1);
//        [Person{id=1002, name='BB'}, Person{id=1001, name='AA'}]
        p1.setName("CC");
//        对于 h1来说 p1改变属性值,并没有对 p1 在h1的hsahCode进行重新计算
        System.out.println(h1);
//        [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]
        h1.remove(p1);
        System.out.println(h1);
//        [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}]

        h1.add(new Person(1001, "CC"));
        System.out.println(h1);
//        [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}]
        h1.add(new Person(1001, "AA"));
        System.out.println(h1);
//        [Person{id=1002, name='BB'}, Person{id=1001, name='CC'}, Person{id=1001, name='CC'}, Person{id=1001, name='AA'}]

    }