集合框架底层与区别优缺点

hashmap
①HashMap的工作原理
HashMap基于hashing原理,我们通过put()get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,让后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。
当两个不同的键对象的hashcode相同时会发生什么? 它们会储存在同一个bucket位置的链表中。键对象的equals()方法用来找到键值对。
因为HashMap的好处非常多,我曾经在电子商务的应用中使用HashMap作为缓存。因为金融领域非常多的运用Java,也出于性能的考虑,我们会经常用到HashMapConcurrentHashMap。(ConcurrentHashMap是线程安全且高效的HashMap)

②HashMapHashtable的区别
HashMapHashtable都实现了Map接口,主要的区别有:线程安全性,同步(synchronization),以及速度。
HashMap几乎可以等价于Hashtable,除了HashMap是非synchronized的,并可以接受null(HashMap可以接受为null的键值(key)和值(value),而Hashtable则不行)HashMap是非synchronized,而Hashtablesynchronized,这意味着Hashtable是线程安全的,多个线程可以共享一个Hashtable;而如果没有正确的同步的话,多个线程是不能共享HashMap的。Java 5提供了ConcurrentHashMap,它是HashTable的替代,比HashTable的扩展性更好。
另一个区别是HashMap的迭代器(Iterator)是fail-fast迭代器,而Hashtable的enumerator迭代器不是fail-fast的。所以当有其它线程改变了HashMap的结构(增加或者移除元素),将会抛出ConcurrentModificationException,但迭代器本身的remove()方法移除元素则不会抛出ConcurrentModificationException异常。但这并不是一个一定发生的行为,要看JVM。这条同样也是EnumerationIterator的区别。
由于Hashtable是线程安全的也是synchronized,所以在单线程环境下它比HashMap要慢。如果你不需要同步,只需要单一线程,那么使用HashMap性能要好过HashtableHashMap不能保证随着时间的推移Map中的元素次序是不变的。
hashmap初始化容量是16而hashtable初始化容量为11,扩容时hashtable变为原来的2+1,而hashmap是原来的2倍!
同时我们要注意的一些重点:
1) sychronized意味着在一次仅有一个线程能够更改Hashtable。就是说任何线程要更新Hashtable时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新Hashtable2) Fail-safe和iterator迭代器相关。如果某个集合对象创建了Iterator或者ListIterator,然后其它的线程试图“结构上”更改集合对象,将会抛出ConcurrentModificationException异常。但其它线程可以通过set()方法更改集合对象是允许的,因为这并没有从“结构上”更改集合。但是假如已经从结构上进行了更改,再调用set()方法,将会抛出IllegalArgumentException异常。
3) 结构上的更改指的是删除或者插入一个元素,这样会影响到map的结构。


最后我们得出了以下结论:
HashtableHashMap有几个主要的不同:线程安全以及速度。仅在你需要完全的线程安全的时候使用Hashtable,而如果你使用Java 5或以上的话,请使用ConcurrentHashMap吧。
ashMap和HashSet的区别是Java面试中最常被问到的问题。如果没有涉及到Collection框架以及多线程的面试,可以说是不完整。HashMapHashSet都是collection框架的一部分,它们让我们能够使用对象的集合。collection框架有自己的接口和实现,主要分为Set接口,List接口和Queue接口。它们有各自的特点,Set的集合里不允许对象有重复的值,List允许有重复,它对集合中的对象进行索引,Queue的工作原理是FCFS算法(First Come, First Serve)。


什么是HashSet
HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。
public boolean add(Object o)方法用来在Set中添加元素,当元素值重复时则会立即返回false,如果成功添加的话会返回true。

什么是HashMap
HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键,但是可以有重复的值。Map接口有两个基本的实现,HashMapTreeMapTreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为nullHashMap是非synchronized的,但collection框架提供方法能保证HashMap synchronized,这样多个线程同时访问HashMap时,也能保证只有一个线程更改Map。

hashmap:在1.8之前hashmap是由数组+链表,1.8之后是数组+红黑树(当链表长度大于阈值时候,默认为8,将链表转化成红黑树,减少搜索时间)

一、HahMap存储对象的过程如下
1、对HahMapKey调用hashCode()方法,返回int值,即对应的hashCode;
2、把此hashCode作为哈希表的索引,查找哈希表的相应位置,若当前位置内容为NULL,则把hashMap的KeyValue包装成Entry数组,放入当前位置;
3、若当前位置内容不为空,则继续查找当前索引处存放的链表,利用equals方法,找到Key相同的Entry数组,则用当前Value去替换旧的Value4、若未找到与当前Key值相同的对象,则把当前位置的链表后移(Entry数组持有一个指向下一个元素的引用),把新的Entry数组放到链表表头;

二、HashSet存储对象的过程
往HashSet添加元素的时候,HashSet会先调用元素的hashCode方法得到元素的哈希值 ,然后通过元素 的哈希值经过移位等运算,就可以算出该元素在哈希表中 的存储位置。
情况1: 如果算出元素存储的位置目前没有任何元素存储,那么该元素可以直接存储到该位置上。
情况2: 如果算出该元素的存储位置目前已经存在有其他的元素了,那么会调用该元素的equals方法与该位置的元素再比较一次,如果equals返回的是true,那么该元素与这个位置上的元素就视为重复元素,不允许添加,如果equals方法返回的是false,那么该元素运行添加。

HashSetHashMap的区别
1.实现了Map接口,另一个实现了set接口
2.hashmap储存键值对,hashset仅仅存储了对象
3.hashmap使用put方法将元素放入集合中,而hashset使用add方法
4.hashmap使用键对象来计算hashcode值,hashset使用成员对象来计算hashcode值,对于两个相同的hashcode来讲,使用equals方法来判断对象的相等性,如果两个对象不同,那么返回false
5.hashmap底层是一个数组结构,数组中的每一项对应了一个链表,数组的特点就是快速读取,而hashset是链表结构特点是易于存储,不易于快速读取!

HashMap的工作原理,HashMapget()方法的工作原理
“HashMap是基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。”这里关键点在于指出,HashMap是在bucket中储存键对象和值对象,作为Map.Entry。

什么叫hash碰撞
当两个不同的key值经过hash运算得到了相同的hash值,第二个值就会放到第一个值的位置上,但是此时第一个值的位置上已经被占了,所以就导致了冲突。

HashMap中碰撞的解决方法:
1.链地址法和拉链法来链接拥有相同hash值的数据
2.使用2次扰动函数来降低hash冲突的概率
3.jdk1.8过后引入了红黑树进一步降低遍历的时间复杂度

hashmap的优缺点
优点:查询速度非常快
缺点:依赖hash算法,需要额外的计算一次hash值,如果处理不当会额外占用空间!

hash()函数
如果使用hashCode取余,那么相当于参与运算的只有hashCode的低位,高位是没有起到任何作用的,所以我们的思路就是让hashCode取值出的高位也参与运算,进一步降低hash碰撞的概率,使得数据分布更平均,我们把这样的操作称为扰动,在1.7中进行了4次位运算,5次异或运算(9次扰动),在1.8中,只进行了1次位运算和1次异或运算(2次扰动)!


“如果两个键的hashcode相同,如何获取值对象?” 
当我们调用get()方法,HashMap会使用键对象的hashcode找到bucket位置,然后获取值对象。如果有两个值对象储存在同一个bucket,将会遍历链表直到找到值对象。找到bucket位置之后,会调用keys.equals()方法去找到链表中正确的节点,最终找到要找的值对象。

“如果HashMap的大小超过了负载因子(load factor)定义的容量,怎么办?”
默认的负载因子大小为0.75,也就是说,当一个map填满了75%的bucket时候,和其它集合类(ArrayList)一样,将会创建原来HashMap大小的两倍的bucket数组,来重新调整map的大小,并将原来的对象放入新的bucket数组中。这个过程叫作rehashing,因为它调用hash方法找到新的bucket位置。

HashMap的扩容操作是怎么实现的?
在1.8中键值对大于阀值就会调用resize方法进行扩容,每次扩容都是2倍,扩展后Node对象的位置要么在原位置,要么移动到原偏移量两倍的位置。

“重新调整HashMap大小存在什么问题吗?”
当多线程的情况下,可能产生条件竞争(race condition)。当重新调整HashMap大小的时候,确实存在条件竞争,因为如果两个线程都发现HashMap需要重新调整大小了,它们会同时试着调整大小。在调整大小的过程中,存储在链表中的元素的次序会反过来,因为移动到新的bucket位置的时候,HashMap并不会将元素放在链表的尾部,而是放在头部,这是为了避免尾部遍历(tail traversing)。如果条件竞争发生了,那么就死循环了。1.8中引入的红黑树解决了多线程死循环问题!


为什么String, Interger这样的wrapper类适合作为键? 
String, Interger这样的wrapper类作为HashMap的键是再适合不过了,而且String最为常用。因为String是不可变的,也是final的,而且已经重写了equals()hashCode()方法了。其他的wrapper类也有这个特点。不可变性是必要的,因为为了要计算hashCode(),就要防止键值改变,如果键值在放入时和获取时返回不同的hashcode的话,那么就不能从HashMap中找到你想要的对象。不可变性还有其他的优点如线程安全。如果两个不相等的对象返回不同的hashcode的话,那么碰撞的几率就会小些,这样就能提高HashMap的性能。

如果使用Object作为HashMapKey,应该怎么办呢?
1.重写hashCode()是因为需要计算存储数据的存储位置
2.重写equals()方法目的是为了保证key在哈希表中的唯一性


我们可以使用自定义的对象作为键吗? 
当然你可能使用任何对象作为键,只要它遵守了equals()hashCode()方法的定义规则,并且当对象插入到Map中之后将不会再改变了。如果这个自定义对象时不可变的,那么它已经满足了作为键的条件,因为当它创建之后就已经不能改变了。

HashMapConcurrentHashMap 的区别
HashMap的键值对允许有null,但是ConCurrentHashMap都不允许!线程安全问题

我们可以使用CocurrentHashMap来代替Hashtable吗?
ConcurrentHashMap越来越多人用了。我们知道Hashtablesynchronized的,但是ConcurrentHashMap同步性能更好,因为它仅仅根据同步级别对map的一部分进行上锁。所以ConcurrentHashMap当然可以代替HashTable,但是HashTable提供更强的线程安全性!hashtable是直接在put和get上面加上了synchronized锁它是一个重量级锁!hashtable是全表锁直接锁住全局,其他线程就要等待导致性能变低,在1.7时候的concurrenthashmap是分段锁,锁住每一个segment数组,在1.8时候采用Node + CAS + Synchronized来保证并发安全进行实现,因为只锁住局部效率更高!

ConcurrentHashMap 底层具体实现?实现原理是什么?
乐观锁机制
1.1.7中首先将数据分为一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。
2.在JDK1.7中,ConcurrentHashMap采用Segment (数组)+ HashEntry(数组)的方式进行实现
在JDK1.8中,放弃了Segment臃肿的设计,取而代之的是采用Node + CAS + Synchronized来保证并发安全进行实现,synchronized只锁定当前链表或红黑二叉树的首节点,这样只要hash不冲突,就不会产生并发,效率又提升N倍。
ConcurrentHashMap继承abstractmap,每次都会判断一次key值是否存在,如果存在就不会欲创建这个node了,头插法插入

我们该如何选择HashMap 还是 TreeMap?
插入删除选择hashmap性能更高
遍历选择treemap性能更高

Arraylist
如何实现数组和 List 之间的转换?
数组转 List:使用 Arrays. asList(array) 进行转换。
List 转数组:使用 List 自带的 toArray() 方法。

arraylist的优点:
它底层是数组,随机访问模式,实现randomaccess接口,查找快,按顺序添加比较快!

arraylist的缺点:
删除或者插入元素时候需要做一次复制操作,如果要复制的元素很多,那么就会比较消耗性能!


ArrayListLinkedList 的区别是什么?
1.ArrayList 是动态数组数据结构,LinkedList是双向链表数据结构!
2.ArrayList 在随机访问时候效率高,linkedlist是线性数据存储方式,需要移动指针从前往后依次查找!
3.非首尾的增加和删除操作,linkedlist要比ArrayList效率要高,ArrayList要影响数组类的其他数据的下标!linkedlist遍历要慢一些!
4.linkedlist更占内存,它的节点出了存储数据,还要存储两个引用,一个指向前一个元素,另一个指向下一个元素!
5.他们都是线程不安全的!

ArrayListVector 的区别是什么?
1.这两个类都实现了 List 接口(List 接口继承了 Collection 接口),他们都是有序集合。
2.Vector 使用了 Synchronized 来实现线程同步,是线程安全的!
3.ArrayList 在性能方面要优于 Vector4.Vector 扩容每次会增加 1 倍,而 ArrayList 只会增加 50%。

多线程场景下如何使用 ArrayListArrayList 不是线程安全的,如果遇到多线程场景,可以通过 Collections 的 synchronizedList 方法将其转换成线程安全的容器后再使用。

ListSet 的区别
set:查询元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变!
list:查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变






hashset
HashSet 的实现原理?
HashSet 是基于 HashMap 实现的!

HashSet如何检查重复?HashSet是如何保证数据不可重复的?
向HashSet 中add ()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles 方法比较。
HashMap 的 key 是唯一的,由源码可以看出 HashSet 添加进去的值就是作为HashMap 的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复

Queuepoll()remove()有什么区别?
1.都是返回第一个元素,并在队列中删除返回的对象。
2.没有元素 poll()会返回 null,而 remove()会直接抛出 NoSuchElementException 异常。




list : 有序,可重复,能存nullArraylist,是动态数组的数据结构,查询快!
vector,数组结构,插入了synchronize修饰,线程安全,性能较差
linkedlist,使用双向链表数据结构实现储存,增删快!

set:无序,不重复,只能存一个null值
hashset:无序不重复是基于hashmap实现
linkedhashset:继承于hashset也是基于hashmap实现的!
treeSet:有序,不重复:内部是红黑树结构!

map:无序,键唯一,不能重复,值可以重复,键和值能存null值
hashmap:数组+红黑树,拉链法
linkedhashmap:继承hashmap,双向链表
hashtable:数组+链表
treemap:红黑树(自平衡的排序二叉树) 







Collection接口
请添加图片描述
Map接口
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值