Java集合(一)

本文详细介绍了Java中的各种集合类,如ArrayList、LinkedList、HashMap、HashTable和ConcurrentHashMap,对比了它们的实现原理、性能优劣以及CRUD操作。还讨论了equals和hashCode的重要性,以及如何使用LinkedHashMap实现LRU缓存策略。

集合主要分为4类:

List,Set,Queue,Map

ArrayList实现原理(1.5倍扩容)

ArrayList底层是一个动态数组,默认初始长度为10,也可以在构造函数里面声明,如果数组被填满,则会创建一个容量为当前的1.5(loadFactor)倍的数组,并使用Arrays.copyOf方法把该数组的内容复制到这个数组里面去,支持随机访问,O(1),但是线程不安全。可以考虑Vector或juc包下的集合。

ArrayList和LinkedList区别

  • ArrayList是采用数组存储的,占用连续的内存空间,可能会产生碎片,占用空间比较小,而LinkedList采用双向链表存储,除了存储数据外还要存储指向前一个数据和后一个数据的指针,占用空间较大
  • ArrayList采用索引寻找数据,所以查找的复杂度为O(1),而LinkedList需要从头或尾遍历才能得到数据,所以为O(n);
  • ArrayList插入,删除的复杂度为O(n),而LinkedList为O(1)

HashMap的实现原理(2倍扩容)

HashMap底层是使用一个长度为16的数组(哈希表)实现,每个数组元素是一个链表或者是一个红黑树的头结点,称为桶;当调用put方法添加数据时,会根据key的hash值取模运算,得到存放位置,当hash冲突的时候会采用链式存储,将冲突的元素用尾插法存到一个链表里面;另外为了避免链表过长导致查询效率下降,当链表长度大于8并且数组长度大于64的时候,链表会转为红黑树,从而减少链表查询的时间复杂度。负载因子为0.75,当容量达到总容量*0.75时会扩容为原来的两倍,当然因为容量改变了,也会重新哈希,即重新计算各个key的位置。

注意:HashMap的不会根据键值的大小来决定位置,只有TreeMap可以,String的hashCode是以每个字符乘以一个常数累加得到的。

HashTable

HashTable初始容量是11,之后每次扩容是2N+1,线程安全的使用synchronized锁

元素为空问题

  • ArrayList和LinkedList元素都可以为空,并且可以有多个;
  • HashMap的键值都可以为null,但是键只能有一个为null,否则会覆盖键,值可以有多个;
  • HashTable的键和值都不能为空,否则抛出空指针异常
  • ConcurrentHashMap键值都不能为null,因为是线程安全的,所以当一个线程调用containsKey(key)的时候,如果这个key本身就不存在,返回false,如果此时另一个线程调用put(key,null),然后线程又会返回true,取决于两个线程的先后顺序,故造成了线程不安全,而ConcurrentHashMap本身就是一个线程安全的,所以都不能为null

集合框架的CRUD

ArrayList和Vector:ArrayList和Vector都是动态数组,区别是Vector线程安全而ArrayList不安全,故性能反之。

对于栈和队列:

栈和队列经常使用ArrayDeque和LinkedList(实现Deque(继承Queue))来使用,当然ArrayDeque也可以当动态数组来使用;

ArrayDeque和LinkedList有栈 | 队列 | 动态数组所有的增加删除方法

栈的增加/删除方法:push | pop,peek访问栈顶元素

队列的增加/删除方法:offer | poll

动态数组的增加/删除/修改/查询方法:add | remove | set(index) | get(index)

集合的增加/删除:add | remove 【因为集合是无序的,所以没有查询方法】

Hash的增加/删除/查询方法:put | remove(key) | get(key)

堆:Queue heap = new PriorityQueue(),默认小顶堆;

通过向构造方法里面传比较器可实现大顶堆;Queue heap = new PriorityQueue((a,b) -> b - a);

hashCode()-equals()-哈希冲突问题

  • 一定要重写equals的同时重写hashCode,否则集合类使用的时候会造成相同的两个对象存储在不同位置,因为默认使用JVM的随机数充当hashCode,集合类先使用hashCode判断,再使用equals判断,方法重写可以使用lombok提供的@EqualsAndHashCode
  • 若hashCode不同,则肯定不是同一个键,也就不会发送哈希冲突
  • 当哈希冲突时,即hashCode相同,这时会通过equals比较,若返回true,则说明是同一个对象,会将新的value值覆盖原来的value值;否则会放到该桶下面的链表中。

使用LinkedHashMap实现LRU

class LRUCache extends LinkedHashMap<Integer, Integer>{
    private int capacity;
    public LRUCache(int capacity){
        super(capacity,0.75F,true);
        this.capacity = capacity;
    }
    public int get(int key){
        return super.getOrDefault(key,-1);
    }
    
    public void put(int key, int value){
        super.put(key,value);
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry<Integer, Integer> eldest) {
        return super.size() > capacity;
    }
}

重写的方法是protected方法,必须继承父类才能用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Munich*

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

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

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

打赏作者

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

抵扣说明:

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

余额充值