Java集合的相关知识

ArrayList 和 Vector 的区别

相同点:1.ArrayList和Vector底层都是用数组实现的。
              2.若开始都是用空构造则默认的数组长度都为10。

不同点:1.Vector是线程安全的(通过实现synchronize方法)而ArrayList是线程不安全的。
               2.ArrayList如果需要扩容新数组的长度是原来数组的1.5倍,而Vector是原来的两倍。

ArrayList, Vector, LinkedList 的存储性能和特性

(1)ArrayList和Vector他们底层的实现都是一样的,都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢。

(2)Vector中的方法由于添加了synchronized修饰,因此Vector是线程安全的容器,但性能上较ArrayList差,因此已经是Java中的遗留容器。

(3)LinkedList使用双向链表实现存储(将内存中零散的内存单元通过附加的引用关联起来,形成一个可以按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,内存的利用率更高),按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。

快速失败 (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包下的容器都是安全失败,可以在多线程下并发使用,并发修改。

HashMap的数据结构

一直到JDK7,HashMap的结构都是基于一个数组以及多个链表的实现,即我们常说的散列链表的方式。而JDK8中采用的是数组+链表/红黑树的方式,也是非线程安全的。当同一个hash值的节点数大于等于8时,将不再以单链表的形式存储了,会被调整成一颗红黑树

HashMap的工作原理

HashMap基于hashing原理,我们通过put()和get()方法储存和获取对象。当我们将键值对传递给put()方法时,它调用键对象的hashCode()方法来计算hashcode,然后找到bucket位置来储存值对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当发生碰撞了,对象将会储存在链表的下一个节点中。 HashMap在每个链表节点中储存键值对对象。

HashMap什么时候进行扩容

当向容器添加元素的时候,会判断当前容器的元素个数,如果大于等于阈值--即当前数组的长度乘以加载因子(默认是0.75)的值的时候,就会自动扩容。扩容后的大小为原来的两倍。

扩容是一个特别耗性能的操作,所以当程序员在使用HashMap的时候,估算map的大小,初始化的时候给一个大致的数值,避免map进行频繁的扩容。

负载因子是可以修改的,也可以大于1,但是建议不要轻易修改,除非情况非常特殊。

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

存元素时:
    Set的add方法有一个boolean的返回值,先判断集合中有没有某个元素,相应的返回true/false。
    List多次调用add(Object)方法时,每次加入的对象按先来后到的顺序排序,也可以插队,即调用add(int index,Object)方法,就可以指定当前对象在集合中的存放位置。
    Map存放用put方法:put(obj key,obj value),每次存储时,要存储一对key/value,不能存储重复的key,这个重复的规则也是按equals比较相等。
取元素时:
    Set取元素时,没法说取第几个,只能以Iterator接口取得所有的元素,再逐一遍历各个元素。
    List除了可以以Iterator接口取得所有的元素,再逐一遍历各个元素之外,还可以调用get(index i)来明确说明取第几个。
    Map取元素时,用get(Object key)方法根据key获得相应的value,也可以获得所有的key的集合,还可以获得所有的value的集合,还可以获得key和value组合成的Map.Entry对象的集合。

Set什么方法来区分重复与否

Set元素是否重复使用 equals()方法进行判断
==:
    对基本类型来说,比较的是值是否相同。对引用类型来说,比较的是地址值是否相同。
equals():
    默认情况下,比较的是地址值,可进行重写,比较的是对象的成员变量值是否相同。

HashSet和TreeSet有什么区别

Hashset的底层是由哈希表实现的,不能保证元素的排列顺序,顺序有可能发生变化,集合元素可以是null,但只能放入一个null。
Treeset 底层是由红黑树实现的,数据是排好序的,不允许放入null值。
HashSet底层是采用HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key。

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

Cloneable是复制对象的,序列化也是针对对象的操作,集合类只是管理对象的一个工具。这两个接口都是针对真实的对象,而不是集合类这样的管理对象的对象。

集合类的Cloneable接口和Serializable接口应该由集合中具体的类型实现,而不是由集合类来实现序列化。假设集合类实现了这两个接口,如果我要生成一个不需要序列化,不需要clone的集合,那么集合类就强行实现,这样有违集合的设计原则。

数组 (Array) 和列表 (ArrayList) 有什么区别?什么时候应该使用 Array 而不是 ArrayList

Array可以包含基本类型和对象类型,ArrayList只能包含对象类型。Array大小是固定的,ArrayList的大小是动态变化的。ArrayList提供了更多的方法和特性,比如:addAll(),removeAll(),iterator()等等。

如果想要保存一些在整个程序运行期间都会存在而且不变的数据,我们可以将它们放进一个全局数组里,但是如果我们单纯只是想要以数组的形式保存数据,而不对数据进行增加等操作,只是方便我们进行查找的话,那么,我们就选择ArrayList。

Collection 和 Collections 的区别

Collection是集合类的上层接口。本身是Set接口和List接口的父接口,里面包含了一些集合的基本操作。

Collections是一个工具类,方法都是用于操作Collection

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值