JavaSE_4_集合

1、Map和ConcurrentHashMap的区别?

Map和ConcurrentHashMap的区别,Map是接口,ConcurrentHashMap是实现类

2、hashMap内部具体如何实现的?

HashMap基于哈希思想,实现对数据的读写,底层采用数组+链表实现,可以存储null键和null值,线程不安全:

  • 当我们往HashMap中put元素时,先根据key的hashCode重新计算hash值,然后根据得到的hash值通过hash&(tab.length–1)计算出对应的数组下标,如果数组该位置上已经存放有其他元素了,那么新元素将以链表的形式按照头插法存入链表,如果数组该位置上没有元素,就直接将新元素放到此数组中的该位置上,当存储的链表长度大于8时会将链表转换为红黑树;(hash方法根据key的hashCode重新计算一次散列,该算法加入了高位计算,防止低位不变,高位变化时,造成的hash冲突)
  • 当我们从HashMap中get元素时,先计算key的hashCode,找到数组中对应位置的某一元素,然后通过key的equals方法在对应位置的链表中找到需要的元素;
  • 当HashMap中的元素越来越多的时候,hash冲突的几率也就越来越高,所以为了提高查询的效率,就要对HashMap的数组进行扩容,当HashMap中元素个数超过(数组大小*加载因子)时,就会进行数组扩容,数组大小默认值为16,加载因子默认值为0.75,把数组的大小扩展为原来的2倍,然后重新计算每个元素在数组中的位置,这是一个非常消耗性能的操作,所以如果我们已经预知HashMap中元素的个数,那么预设数组的合适长度能够有效的提高HashMap的性能。

3、如果HashMap的key是一个自定义的类,怎么办?

要重写equals方法和hashcode方法,以保证equals相等的对象其hashcode一定也相等。

4、ArrayList和LinkedList的区别,如果一直在list的尾部添加元素,用哪个效率高

ArrayList底层时数组实现的,LinkedList底层是双向链表实现的,ArrayList执行修改和查找的效率比较高,LinkedList执行增加和删除的效率比较高。

如果一直在list尾部增加元素,ArrayList效率更高,但是如果需要在指定位置添加元素时,LinkedList效率更高。

5、请你解释HashMap的容量为什么是2的n次幂?

  • HashMap默认大小为16,又是以原大小的2倍进行扩容,所以默认情况下,HashMap的容量总是2的n次幂;
  • 如果HashMap初始化大小不是2的n次幂也会自动将传入容量转换为2的n次幂,因为计算HashMap下标的求余操作(hash%length)效率不高,源码中优化成了与操作(hash&(length-1)),但是这两个操作能等价的前提是length为2的n次幂。

6、HashMap中为什么有可能产生死循环?

在单线程情况下,只有一个线程对HashMap的数据结构进行操作,是不可能产生闭合回路的,只有在多线程并发的情况下才会出现这种情况;那就是在put操作的时候,如果size>Capacity*loadFactor,那么这时候HashMap就会进行rehash操作,随之HashMap的结构就会发生翻天覆地的变化,很有可能存在两个线程在这个时候同时触发了rehash操作,产生闭合回路。

下图为单线程下的扩容过程:

                        

下图为两个线程并发下的扩容过程(无死循环):

下图为两个线程并发下的扩容过程(有死循环):

7、ConcurrentHashMap锁加在了哪些地方?

  • JDK1.7中,ConcurrentHashMap采用分段锁机制,实现并发的更新操作,底层采用数组+链表的存储结构;将数组分成若干(默认16)个segment,每个segment的容量默认为16,segment继承了ReentrantLock,表明每个segment都可以当做一个锁,这样对于每个segment中的数据需要同步操作时,都是使用每个segment容器对象自身的锁来实现的,只有对全局需要改变时才锁定所有的segment(比如计算size)。
  • JDK1.8中,ConcurrentHashMap抛弃了segment分段锁机制,利用CAS+Synchronized来保证并发更新的安全,底层采用数组+链表+红黑树的存储结构。

8、TreeMap底层,红黑树原理?

9、concurrenthashmap有啥优势,1.7,1.8区别?

底层采用分段数组+链表实现,线程安全;通过把整个Map分为N个Segment,可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。(读操作不加锁,由于HashEntry的value变量是volatile的,能保证读取到最新的值;写操作加锁。);ConcurrentHashMap允许多个修改操作并发进行。

在JDK1.7版本中,ConcurrentHashMap的数据结构是由一个Segment数组和多个HashEntry组成,Segment数组分割成多个小的table来进行加锁,也就是锁分离技术,而每一个Segment元素存储的是HashEntry数组+链表。

 

在JDK1.8版本中,摒弃了Segment的概念,而是使用Node数组+链表+红黑树的数据结构来实现,并发控制使用Synchronized和CAS来操作。

10、ArrayList是否会越界?

可能会越界,在多线程操作同一个ArrayList的时候,由于ArrayList的add()方法没有同步锁,所以多线程调用这个方法的时候,可能存在多个线程拿到相同的size值去判断需要的容量(size+1)是否大于ArrayList的容量,而执行到 elemntData[size++] = e 时却是有序的,这时,由于ensureCapacityInternal()没有适当的扩大ArrayList的容量,从而导致插入数据的长度大于ArrayList的剩余容量,于是也就抛出了越界的异常。

11、什么是TreeMap?

12、ConcurrentHashMap的原理是什么?

ConcurrentHashMap使用锁分段技术,将数据分成一段一段的存储,给每一段数据配置一把锁,当一个线程占用锁访问其中一段数据时,其他段的数据也能被其它线程访问。

ConcurrentHashMap是由Segment数组结构和HashEntry数组结构组成;Segment是一种ReentrantLock,HashEntry用于存储键值对数据,一个ConcurrentHashMap里包含一个Segment数组,Segment的结构与HashMap类似,是一种链表散列数据结构,一个 Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,每个Segment维护着一个HashEntry数组里的元素,当要对HashEntry的数据进行修改时,就必须先获得对应的Segement锁。

13、Java集合类框架的基本接口有哪些?

总共有两大接口:Collection和Map,一个元素集合,一个是键值对集合;其中List和Set接口继承了Collection接口,一个是有序元素集合,一个是无序元素集合;而ArrayList和LinkedList实现了List接口,HashSet实现了Set接口,这几个都比较常用;HashMap和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好。

14、为什么集合类没有实现Cloneable和Serializable接口?

克隆或者是序列化的语义和含义是跟具体的实现相关的;因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。

15、什么是迭代器?

可迭代是Java集合框架下所有集合类的一种共性,也就是把集合中的所有元素遍历一遍。

迭代器(Iterator)模式,又叫游标模式,它的含义是,提供一种方法访问一个容器对象中各个元素,而又不需要暴露该对象的内部细节;简单的说,迭代器就是一个接口,实现了该接口的类就叫做可迭代类,这些类多数时候指的就是java.util包下的集合类;java通过提供Iterator和Iterable俩个接口来实现集合类的可迭代性,迭代器主要的用法是:首先用hasNext()方法作为循环条件,再用next()方法得到每一个元素,最后在进行相关的操作。

16、Iterator和ListIterator的区别是什么?

 

List和Set都能通过iterator()来取得其迭代器;对List来说,也可以通过ListIterator()取得其迭代器,两种迭代器在有些时候是不能通用的,Iterator和ListIterator主要区别在以下方面:

  1. ListIterator有add()方法,可以向List中添加对象,而Iterator不可以;
  2. ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历,而Iterator不可以;
  3. ListIterator可以定位当前的索引位置,通过nextIndex()和previousIndex()实现,而Iterator不可以;
  4. 都可实现删除对象,但是ListIterator可以实现对象的修改,通过set()方法实现,而Iierator仅能遍历,不能修改。

17、快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?

快速失败:在用迭代器遍历一个集合对象时,如果遍历过程中对集合对象的内容进行了修改(增加、删除、修改),则会抛出Concurrent Modification Exception;

  • 原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个 modCount 变量,集合在被遍历期间如果内容发生变化,就会改变modCount的值,每当迭代器使用hashNext()/next()遍历下一个元素之前,都会检测modCount变量是否为expectedmodCount值,是的话就返回遍历,否则抛出异常,终止遍历;
  • 注意:这里异常的抛出条件是检测到 modCount != expectedmodCount 这个条件,如果集合发生变化时修改modCount值刚好又设置为了expectedmodCount值,则异常不会抛出,因此,不能依赖于这个异常是否抛出而进行并发操作的编程,这个异常只建议用于检测并发修改的bug;
  • 场景:java.util包下的集合类都是快速失败的,不能在多线程下发生并发修改(迭代过程中被修改)。

安全失败:采用安全失败机制的集合容器,在遍历时不是直接在集合内容上访问的,而是先复制原有集合内容,在拷贝的集合上进行遍历;

  • 原理:由于迭代时是对原集合的拷贝进行遍历,所以在遍历过程中对原集合所作的修改并不能被迭代器检测到,所以不会触发Concurrent Modification Exception;
  • 缺点:基于拷贝内容的优点是避免了Concurrent Modification Exception,但同样地,迭代器并不能访问到修改后的内容,即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的;
  • 场景:java.util.concurrent包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

18、HashMap和Hashtable有什么区别?

  • HashMap继承于AbstractMap抽象类,Hashtable继承于Dictionary字典,实现Map接口
  • HashMap是非线程安全的,HashTable是线程安全的(通过synchronized实现);
  • HashMap的键和值都允许有null值存在,而HashTable则不行;
  • 因为线程安全的问题,HashMap效率比HashTable的要高;
  • HashMap适合于单线程环境,而Hashtable适合于多线程环境。
一般不建议使用HashTable,因为
  1. HashTable是遗留类
  2. 多线程环境下有同步的ConcurrentHashMap替代

19、ArrayList,Vector,LinkedList的存储性能和特性是什么? 

  • ArrayList和Vector都是使用数组方式存储数据,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素的移动等内存操作,所以索引数据快而插入数据慢;
  • Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,Vector属于遗留容器,已经不推荐使用,但是由于ArrayList和LinkedListed都是非线程安全的,如果遇到多个线程操作同一个容器的场景,则可以通过工具类Collections中的synchronizedList方法将其转换成线程安全的容器后再使用(这是对装潢模式的应用,将已有对象传入另一个类的构造器中创建新的对象来增强实现);
  • LinkedList使用双向链表实现存储,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

20、Collection 和 Collections的区别。 

  • java.util.Collection是一个集合接口,它提供了对集合对象进行基本操作的通用接口方法,Collection接口在Java 类库中有很多具体的实现,Collection接口的意义是为各种具体的集合提供最大化的统一操作方式,其直接继承接口有List与Set;
  • java.util.Collections是一个包装类,它包含各种有关集合操作的静态方法,此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于Java的Collection框架。

21、你所知道的集合类都有哪些?主要方法?

Collection接口:

Collection是最基本的集合接口,一个Collection代表一组Object,Java SDK不提供直接继承自Collection的类,Java SDK提供的类都是继承自Collection的子接口,如List和Set,所有实现Collection接口的类都必须提供两个标准的构造函数:无参数的构造函数用于创建一个空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素,后一个构造函数允许用户复制一个Collection,Collection支持一个iterator()的方法,该方法返回一个迭代器,使用该迭代器即可逐一访问Collection中的每一个元素;

List接口:

List是有序的Collection,可以使用索引来访问List中的元素,类似于Java的数组,List允许存放相同的元素,除了具有Collection接口必备的iterator()方法外,List还提供一个ListIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator允许添加,删除,设定元素,还能向前或向后遍历,实现List接口的常用类有LinkedList,ArrayList,Vector和Stack;

LinkedList类:

LinkedList实现了List接口,允许null元素,LinkedList主要提供了增加、删除、修改、查找等方法,LinkedList没有同步方法,如果多个线程同时访问一个List,必须自己实现访问同步,一种解决方法是在创建List时构造一个同步的List【List list = Collections.synchronizedList(new LinkedList(...))】;

ArrayList类:

ArrayList实现了可变大小的数组,允许null元素,ArrayList主要提供了增加、删除、修改、查找等方法,每个ArrayList实例都有一个容量(Capacity),这个容量可随着不断添加新元素而自动增加,当需要插入大量元素时,在插入前通过调用ensureCapacity方法来增加ArrayList的容量,和LinkedList一样,ArrayList也是非同步的;

Vector类:

Vector类似ArrayList,但是Vector是同步的,由Vector创建的Iterator,和ArrayList创建的Iterator是同一接口,但是因为Vector是同步的,当一个Iterator被创建而且正在被使用时,另一个线程改变Vector的状态,这时调用Iterator的方法将抛出异常;

Stack 类:

Stack继承自Vector,实现了一个后进先出的堆栈,Stack提供了基本的push和pop方法,以及peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置,Stack刚创建后是空栈;

Set接口:

Set是一种不包含重复元素的Collection,Set最多有一个null元素,传入的Collection参数不能包含重复的元素; 

Map接口:

Map提供key到value的映射,一个Map中不能包含相同的key,每个key只能映射一个value;

Hashtable类:

Hashtable继承Map接口,实现了一个key-value映射的哈希表,任何非空(non-null)的对象都可作为key或value,添加数据使用put(key, value),取出数据使用get(key),要同时重写equals方法和hashCode方法,Hashtable是同步的;

HashMap类:

HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null元素;

WeakHashMap类:

WeakHashMap是一种改进的HashMap,它对key实行弱引用,如果一个key不再被外部所引用,那么该key可以被GC回收。

22、List、Set、Map是否继承自Collection接口?

List和Set继承了Collection接口,Map没有继承Collection接口。

23、List、Map、Set三个接口存取元素时,各有什么特点?

存放时:

  • List以特定的索引来存放元素有顺序的存放,可以有重复的元素;
  • Set存放元素是无序的,而且不可重复;
  • Map保存键值对的映射,映射关系可以是一对一或者多对一,需要注意的是,键无序不可重复,值可重复;

取出时:

  • List取出元素可通过for循环,foreach循环和Iterator迭代器迭代;
  • Set取出元素可通过foreach循环,Iterator迭代器迭代;
  • Map取出元素需要转换为keySet或者entrySet,然后进行Iterator迭代器迭代或者foreach循环。

et 和List 都继承了Conllection

转载于:https://www.cnblogs.com/liumilk/p/10654299.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值