Java知识点之集合

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

(1)List以特定的索引来存取元素,可以有重复元素
(2)Set不能存放重复元素(用对象的equals()方法来区分元素是否重复)
(3)Map保存键值对映射,映射关系可以是一对一或者多对一
(4)Set和Map容器都有基于哈希存储和排序树的两种实现版本,基于哈希存储的版本理论存取时间复杂度为o(1),而基于排序树版本的实现在插入和删除元素时会按照元素或元素的键构成排序树从而达到排序和去重的效果
(5)对于HashSet,底层就是一个HashMap,set的值为Map的键,Map的值为一个静态final的Object对象,所以Set数值不能重复

List其他

list在sout直接打印的时候,如果泛型(引用型对象)没有重写toString()方法,那么打印的为本对象(类名@hashCode),重写了的话即为重写了的方法

Set其他
HashSet

(1)HashSet底层默认为HashMap
(2)桶+链表+二叉树
1)桶(数组)的默认大小为16(1 << 4),最大(2<<30),扩容因子0.75,当总容量达到75%的时候就开始扩容,扩容每次两倍
2)链表:hash算法值相同的时候,会把值相同的放在同一个链表上,链表上的元素个数超过8个的时候转化为二叉树,二叉树的查找速度快

TreeSet

(1)对于自定义对象,需要实现Comparable方法,重写compareTo方法,一般情况下通过临时变量temp(this.age-o.age)来进行多重判断,判断每一个需要比较的属性
(2)TreeSet中的compareTo方法对于返回0的情况会去重

2、阐述ArrayList、Vector、LinkedList的存储性能和特性

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

3、请判断List、Set、Map是否继承自Collection接口?

List,Set实现了Collection接口,Map没有。Map是键值对映射容器,与List,Set有明显的区别,而Set存储的零散的元素且不允许有重复元素,List是线性结构的容器,适用于按数值索引访问元素的情形。

4、请说明Collection 和 Collections的区别。

(1)Collection是集合类的上级接口,继承于他的接口主要有Set和List
(2)Collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作

5、请说明ArrayList和LinkedList的区别?如果一直在list的尾部添加元素,用哪种方式的效率高?

相同点

(1)ArrayList和LinkedList都实现了List接口

不同点

(1)ArrayList是基于索引的数据接口,它的底层是数组。它可以以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n)
(2)相对于ArrayList,LinkedList的插入、添加、删除操作更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者是更新索引;但是LinkedList更占内存,因为LinkedList为每一个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素
(3)LinkedList底层为一个双向链表,当要获取某个index的元素时,首先判断索引位是否小于元素总量的一半,如果是的话则从头部开始遍历,否则从尾部开始遍历
(4)ArrayList采用数组实现,LinkedList采用双向链表实现。ArrayList遍历效率高于LinkedList,LinkedList插入和删除的效率比ArrayList要高,一直在list的尾部添加元素,LinkedList的效率高
(5)当需要存储大量数据的时候,用ArrayList有参构造函数进行初始化,因为对于ArrayList的无参构造函数,默认数组里面的数据为空(0个),当进行添加第一个数据的时候,就默认给数组初始化大小为10。当数组中的元素的大小大于10以后,每次就会进行1.5倍扩容(int newCapacity = oldCapacity + (oldCapacity >> 1))(右移一位代表除以2,左移一位代表乘以2),数组允许的最大值为(Integer.MAX_VALUE - 8),大约20亿,扩容过程就是数组的拷贝
在这里插入图片描述
在这里插入图片描述

6、请你介绍一下map的分类和常见的情况

HashMap implements Map

(1)HashMap是最常用的一个Map,它根据键的hashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。HashMap最多只允许一条记录键位null,允许多条记录的值为null
在这里插入图片描述
(2)HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap,可能会导致数据的不一致性。如果需要同步,可以用Collections的synchronizedMap具有同步功能

Hashtable implements Map

(1)HashTable与HashMap类似,不同的是它不允许记录的键或值为null
在这里插入图片描述
(2)它支持线程的同步,即任一时刻只有一个线程能写HashTable,因此也导致了HashTable在写入时会比较慢

LinkedHashMap implements Map

LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的

TreeMap implements Map

TreeMap实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录时排过序的。

7、请你说明HashMap和Hashtable的区别?

相同点

HashMap和Hashtable都实现了Map接口

不同点

HashMap允许键和值是null,而Hashtable不允许值或者键为null;Hashtable是同步的,而HashMap不是,因此HashMap更适合于单线程环境,而Hashtable适合于多线程环境;HashMap提供了可供应用迭代的键的集合,Hashtable提供了对键的列举

8、请说说快速失败(fail-fast)和安全失败(fail-safe)的区别?

Iterator的安全失败是基于对底层集合做拷贝,因此,它不受源集合上修改的影响。java.util包下面的所有的集合类都是快速失败的,而java.util.cucrrent包下面的所有的类都是安全失败的。快速失败的迭代器会抛出ConcurrentModificationException异常,而安全失败的迭代器永远不会抛出这样的异常

9、请你说说Iterator和ListIterator的区别?

(1)Iterator可以用来遍历Set和List,而ListIterator只能用来遍历List
(2)Iterator对集合只能是向前遍历,ListIterator既可以向前也可以向后
(3)ListIterator实现了Iterator接口,并包含了其他的功能,比如:增加元素、替换元素,获取前一个和后一个元素的索引等

10、请简单说明一下什么是迭代器?

(1)Iterator提供了统一遍历操作集合元素的统一接口,Collection接口实现Iterable接口(Iterator为Itarable中的变量)
(2)每个集合都通过实现Iterable接口中的iterator方法返回iterator接口的实例,然后对集合的元素进行迭代操作,需要注意的是:在迭代元素的时候不能通过集合的方法删除元素,否则会抛出ConcurrentModificationException异常,但是可以通过Iterator接口中的remove()方法进行删除

11、请你说明一下ConcurrentHashMap的原理?

(1)ConcurrentHashMap 类中包含两个静态内部类 HashEntrySegmentHashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。
(2)一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组。HashEntry 用来封装散列映射表中的键值对。在 HashEntry 类中,key,hash 和 next 域都被声明为 final 型,value 域被声明为 volatile 型
(3)Segment 类继承于 ReentrantLock 类,从而使得 Segment 对象能充当锁的角色。每个 Segment 对象用来守护其(成员对象 table 中)包含的若干个桶
(4)Java并发包中的,既是线程安全的,又不至于效率太低
(5)底层采用的是分段锁机制,即只加载某一段数据上
读锁:共享锁,一个线程进行读的时候,不影响其他线程的结构
写锁:排它锁,一个线程在进行写的时候,不允许其他线程的操作

12、请说明ArrayList是否会越界?

(1)ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构。对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针,ArrayList并发add()可能出现数组下标越界异常。
(2)ArrayList在初始化的时候默认数组中的元素为0,当在添加第一个元素的时候,会将数组初始化为10,之后每次添加的时候会做一次判断,判断数组下一次添加是否满了,是的话则1.5倍扩容

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

负载因子默认是0.75, 2^n是为了让散列更加均匀,例如出现极端情况都散列在数组中的一个下标,那么hashmap会由O(1)复杂退化为O(n)的。

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

使用HashMap,如果key是自定义的类,就必须重写hashCode()和equals()

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值