1.Java容器学习笔记(自用嘿嘿嘿!!!)

本文深入解析Java集合框架,包括List、Set、Map的区别与实现,ArrayList与LinkedList的特性,Hashset与TreeSet的底层实现,以及HashMap、Hashtable、ConcurrentMap的对比。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、集合框架

1、List、set区别和实现类

(1)List是数组加链表构成,他有两个实现类,分别是ArrayList、LinkedList。
(2)Set有两个实现类,分别是HashSet、TreeSet。
(3)区别:list有序并且可重复,Set无序并且不可重复(Treeset提供有序的存储)

2、ArrayList和linkedList区别,底层实现,优点

(1)ArrayList适合查找和修改数据,因为ArrayList底层是一个动态数组,所以根据索引查找速度就会快,时间复杂度为O(1),而插入和删除最好时间复杂度是O(1),在最后一个位置,最坏时间复杂度为O(n),在第一个位置,原因是数组当前改变位置后面元素都要移动。
(2)LinkedList适合插入和删除数据,因为LinkedList底层是一个链表,当插入和删除数据时,时间复杂度为O(1),因为其他节点不用移动,但查找和修改时间复杂度为O(n),因为要遍历链表。

3、Hashset,TreeSet底层

(1)Hashset底层是hashmap,一个hash表
说白了,HashSet就是限制了功能的HashMap,所以了解HashMap的实现原理,这个HashSet自然就通对于HashSet中保存的对象,主要要正确重写equals方法和hashCode方法,以保证放入Set对象的唯一性。虽说是Set是对于重复的元素不放入,倒不如直接说是底层的Map直接把原值替代了(这个Set的put方法的返回值真有意思)HashSet没有提供get()方法,愿意是同HashMap一样,Set内部是无序的,只能通过迭代的方式获得
(2)TreeSet底层是Treemap,一个树,提供了有序存储
TreeSet中存放的元素是有序的(不是插入时的顺序,是有按关键字大小排序的),且元素不能重复。 而如何实现有序存储,就需要有一个比较器,其实说起来,TreeSet更受关注的是不重复且有序,这个有序就需要有一个compare的过程,因此会需要参数实现Comparable接口。

4、Map底层实现

Map有3个实现类,分别是Hashmap、Hashtable、concurrentMap。

5、ConcurrentMap,hashMap,Hashtable区别

(1)HashMap底层是数组加链表(哈希表),Java1.8之后当链表长度超过8的时候,为提高查找效率,改为用红黑树存储,因为红黑树查找效率高,但HashMap是线程不安全的。
允许存储null
(2)Hashtable是线程安全的,其数据结构和hashmap相同,并且不能存null值,之所以线程安全的,是hashtable加了synchronized锁,加在了方法上。 HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞或轮询状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素,所以竞争越激烈效率越低。
(3)Concurrenthashmap是由在Java1.7之前是Segment(他是继承自Reentrantlock)数组结构和HashEntry数组结构组成,每个hashentry里面又是链表结构,采用分段锁技术,把HashMap分割成若干个Segment,在put的时候需要锁住Segment,get时候不加锁,使用volatile来保证可见性,当要统计全局时(比如size),首先会尝试多次计算modcount来确定,这几次尝试中,是否有其他线程进行了修改操作,如果没有,则直接返回size。如果有,则需要依次锁住所有的Segment来计算。Java1.8之后摒弃了这个机制,不采用segment而采用node数组和CAS,锁住node来实现减小锁粒度。使用3个CAS操作来确保node的一些操作的原子性,这种方式代替了锁。

例子:
(1)hashtable :两个线程A、B,一个循环读,一个循环写,同时进行,有没有线程安全问题?为什么?
有,因为在添加(写入)的时候,一先增加总数(count++),二再写入数组(items[count - 1] = val)。这种情况,它就对读操作就不是线程安全的。
因为另外一个线程在进行GetLast读操作的时候,可能正好发生在一和二操作之间,它将返回的,将是一个未定义的元素。
不安全例子:
class MyList
{
int count;
int[] items = new int[1024];
public void Add(int val)
{
count++;
items[count - 1] = val;
}
public int GetLast()
{
return items[count - 1];
}
}
(2)为什么先比hashcode再比equals?Equal相等hashcode一定相等吗?Hashcode相等equals一定相等吗?
首先要了解hash表结构好吧?因为先比的是hash地址,找到对应的地址在比较值的大小,因为先比的是地址,所以hashcode相等,equal一定相等,相反,不同地址可能有相等的值,所以equals相等hashcode一定不相等。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值