Java集合核心知识点整理

1.集合思维导图

在这里插入图片描述

2.List详解

List特点:元素有序,可重复

2.1 ArrayList(工作中常用,面试常问)

简单的分析一下源码(JDK1.8)

一、创建ArrayList


    /**
     * Constructs an empty list with an initial capacity of ten.
     */
     //空参构造函数创建
    public ArrayList() {
    //JDK1.8只是初始化了一个空的Object[]数组,并没有给初始容量10.上面注释是JDK1.7的,官方改了代码没改注释。
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

二、向ArrayList里添加元素

    public boolean add(E e) {
    //判断是否需要进行扩容,此时因为是空数组,所以size是0
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //把元素添加到数组中
        elementData[size++] = e;
        return true;
    }
    //此时minCapacity==1
    private void ensureCapacityInternal(int minCapacity) {
 //calculateCapacity 计算容量
 ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    //计算容量的方法
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //如果此时数组是Object[]
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //返回10和传进来的参数(1)较大的,此时返回10
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    //此时minCapacity==10
    private void ensureExplicitCapacity(int minCapacity) {
 
        modCount++;

        // overflow-conscious code
        //10-0=10>0,故进入grow方法进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    //此时minCapacity==10
    private void grow(int minCapacity) {
        // overflow-conscious code
        //oldCapacity ==0
        int oldCapacity = elementData.length;
        //newCapacity ==0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //此时0-10<0
        if (newCapacity - minCapacity < 0)
        //newCapacity ==10
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //把旧的数组复制给新的数组,然后容量是10
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

小结:JDK1.8在创建空参的ArrayList时,只是初始化了空数组,没有给初始容量,当第一次调用add()方法时,给初始容量为10,当第11次调用add方法时,才会第二次扩容,扩容至原容量的1.5倍

底层:动态数组
特点:线程不安全、查询效率高,增删效率低
支持快速随机访问:get(int index)

2.2 LinkedList(工作中基本不会用,面试也不常问)

数据结构:双向链表
扩容机制:无需扩容
特点:插入和删除操作比ArrayList效率高,线程不安全

2.3 Vector(几乎淘汰)

扩容机制:按原容量2倍扩容
特点:线程安全,效率低
只是相比ArrayList线程安全,因为其方法都加了synchronized,所以效率低。

3.Set详解

Set特点:元素无序,不可重复

3.1 HashSet

底层是HashMap
特点:线程不安全,可以存null值
HashSet为什么是无序的?
在这里插入图片描述
在这里插入图片描述
这段代码的输出结果和我们添加的顺序不一致,但这和Set无序并无因果关系。
实际上,我们在add元素时,存储的数据在底层数组中是根据数据的HashCode进行排序的。
HashSet添加元素的过程:当添加元素时,首先会调用元素所在类的hashcode方法,计算此元素的哈希值,此哈希值通过某种算法计算出在HashSet底层数组中的下标,然后看此下标有无值,如果没有,直接插入,如果有,则会调用此元素的equals方法,如果是true,则添加失败,如果false,则添加成功。所以,Hashset是不重复的。

3.2 LinkedHashSet

在原HashSet的底层基础上,加了两个指针,一个指向前一个元素,一个指向后一个元素。

3.3 TreeSet

数据结构:红黑树(平衡二叉树)
特点:确保集合元素是排序状态
排序方法:自然排序(实现Comparable接口)、定制排序(Comparator)
自然排序(实现Comparable接口):

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet<User> set = new TreeSet<>();
        set.add(new User("eason",23));
        set.add(new User("bill",24));
        set.add(new User("jame",17));
        set.add(new User("jame",18));
        Iterator<User> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}
@Data
public class User implements Serializable,Comparable<User> {
    private String name;
    private int age;

    public User(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;
        User user = (User) o;
        return age == user.age && name.equals(user.name);
    }

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


    @Override
    public int compareTo(User u) {
        int i = this.name.compareTo(u.name);
        if (i==0){
            return this.age- u.age;
        }
        return i;
    }
}

定制排序:

public class TreeSetTest {
    public static void main(String[] args) {
        TreeSet<User> set = new TreeSet<>(new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                int i = o1.getName().compareTo(o2.getName());
                if(i==0){
                    return o1.getAge()-o2.getAge();
                }
                return i;
            }
        });

        set.add(new User("eason",23));
        set.add(new User("bill",24));
        set.add(new User("jame",17));
        set.add(new User("jame",18));
        Iterator<User> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

    }
}

4.Map

特点:双列数据,键值对形式

4.1 HashMap

数据结构及添加元素过程:

JDK1.8之前:数组+链表。使用Entry数组来存储key-value,每一个键值对组成了一个Entry实体,Entry类实际上是一个单向的链表结构,它具有next指针,指向下一个Entry实体,以此来解决Hash冲突的问题。HashMap实现一个内部类Entry,重要的属性有hash、key、value、next。
通过hash值找到当前元素存放的位置,如果位置上有值,则比较hash值和key值,如果hash值和key都相同,则直接覆盖。如果不同,则将要插入的数据加到链表中。

JDK1.8之后:数组+链表+红黑树。当链表长度大于阈值(默认为8)时,判断当前数组的长度是否小于64,如果小于64,先进行数组扩容,大于等于64,将链表转换为红黑树,为了减少搜索时间。

特点:线程不安全,效率高,可以存null键和null值,key不能重复,value可以重复。
扩容机制:

JDK1.8无参构造时,不再给定默认容量16,只初始化了加载因子等于0.75,当第一次put时,创建长度为16的数组,数组类型是Node[],之后按2倍扩容。
有参构造时,根据传入的容量计算一个大于等于该容量的最小的2的N次方。例如:传3,容量为4,传4,容量为4,传5,容量为8…之后按2倍扩容。

4.2 为什么要改成数组+链表+红黑树呢?

为了提升当发生hash冲突严重时,链表过长,查找性能低。

4.2 LinkedHashMap

在原有的HashMap底层结构上,添加了一堆指针,指向前一个和后一个元素,对于频繁的便利操作,效率高于HashMap。

4.3 ConcurrentHashMap

特点:线程安全

4.4 TreeMap

数据结构:底层是红黑树
特点:按照添加的key-value进行排序

4.5 HashTable(基本废弃)

特点:线程安全,效率低,不能存null键和null值
默认值是11,扩容为2倍+1

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IDEA上的操作工

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值