欢迎关注我,一块儿学习
《提高能力,涨薪可待 》
《面试知识,工做可待 》
《实战演练,拒绝996》
也欢迎关注微信公众号【 Ccww笔记】,原创技术文章第一时间推出
若是此文对你有帮助、喜欢的话,那就点个赞呗,点个关注呗!
是否是感受在工做上难于晋升了呢?
是否是感受找工做面试是那么难呢?
是否是感受本身天天都在996加班呢?在工做上必须保持学习的能力,这样才能在工做获得更好的晋升,涨薪指日可待,欢迎一块儿学习【提高能力,涨薪可待】系列java
redis面试
Java并发redis
在找工做面试应在学习的基础进行总结面试知识点,工做也指日可待,欢迎一块儿学习【 面试知识,工做可待】系列
最后,理论知识到准备充足,是否是该躬行起来呢?欢迎一块儿学习【 实战演练,拒绝996】系列
Java 集合类提供了一套设计良好的支持对一组对象进行操做的接口和类。Java集合类里面最基本的接口有:算法
集合类接口指定了一组叫作元素的对象。集合类接口的每一种具体的实现类均可以选择以它本身的方式对元素进行保存和排序,可使得集合类很灵活,能够实现自定义集合类属性,好比有的集合类容许重复的键,有些不容许。spring
Java5 引入了泛型,全部的集合接口和实现都大量地使用它。泛型容许咱们为集合提供一个能够容纳的对象类型。所以,若是你添加其它类型的任何元素,它会在编译时报错。这避免了在运行时出现 ClassCastException,由于你将会在编译时获得报错信息。mongodb
泛型也使得代码整洁,咱们不须要使用显式转换和 instanceOf 操做符。它也给运行时带来好处,由于不会产生类型检查的字节码指令。编程
Iterator 接口提供了不少对集合元素进行迭代的方法。每个集合类都包含了能够返回迭代器实例的 迭代方法。迭代器能够在迭代的过程当中删除底层集合的元素。segmentfault
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。所以,应该由集合类的具体实现来决定如何被克隆或者是序列化。数组
下面列出了他们的区别:安全
Iterator 的安全失败是基于对底层集合作拷贝,所以,它不受源集合上修改的影响。
java.util 包下面的全部的集合类都是快速失败的,而 java.util.concurrent 包下面的全部的类都是安全失败的。快速失败的迭代器会抛出 ConcurrentModificationException 异常,而安全失败的迭代器永远不会抛出这样的异常。
Enumeration 速度是 Iterator 的2倍,同时占用更少的内存。可是,Iterator 远远比 Enumeration 安全,由于其余线程不可以修改正在被 iterator 遍历的集合里面的对象。同时,Iterator 容许调用者删除底层集合里面的元素,这对 Enumeration 来讲是不可能的。
根据应用的须要正确选择要使用的集合的类型对性能很是重要,好比:假如元素的大小是固定的,并且能事先知道,咱们就应该用 Array 而不是 ArrayList。 有些集合类容许指定初始容量。所以,若是咱们能估计出存储的元素的数目,咱们能够设置初始容量来避免从新计算 hash 值或者是扩容。
为了类型安全,可读性和健壮性的缘由老是要使用泛型。同时,使用泛型还能够避免运行时的 ClassCastException。
使用 JDK 提供的不变类(immutable class)做为Map的键能够避免为咱们本身的类实现 hashCode() 和 equals() 方法。
编程的时候接口优于实现。
底层的集合其实是空的状况下,返回长度是0的集合或者是数组,不要返回 null。
HashMap(数组+链表+红黑树)的结构,利用了红黑树,因此其由 数组+链表+红黑
树组成:
HashMap 里面是一个数组,而后数组中每一个元素是一个单向链表。上图中,每一个绿色
的实体是嵌套类 Entry 的实例, Entry 包含四个属性: key, value, hash 值和用于单向链表的 next。
HashMap 根据键的 hashCode 值存储数据,大多数状况下能够直接定位到它的值,于是具备很快
的访问速度,但遍历顺序倒是不肯定的。
HashMap 最多只容许一条记录的键为 null,容许多条记
录的值为 null。
HashMap 非线程安全,即任一时刻能够有多个线程同时写 HashMap,可能会导
致数据的不一致。若是须要知足线程安全,能够用 Collections 的 synchronizedMap 方法使
HashMap 具备线程安全的能力,或者使用 ConcurrentHashMap。
查找的时候,根据 hash 值咱们可以快速定位到数组的
具体下标,可是以后的话, 须要顺着链表一个个比较下去才能找到咱们须要的,时间复杂度取决
于链表的长度,为 O(n)。
为了下降这部分的开销,在 Java8 中, 当链表中的元素超过了 8 个之后,
会将链表转换为红黑树,在这些位置进行查找的时候能够下降时间复杂度为 O(logN)。
Java 中的 HashMap 使用 hashCode() 和 equals() 方法来肯定键值对的索引,当根据键获取值的时候也会用到这两个方法。若是没有正确的实现这两个方法,两个不一样的键可能会有相同的 hash 值。
所以,可能会被集合认为是相等的。并且,这两个方法也用来发现重复元素。因此这两个方法的实现对 HashMap 的精确性和正确性是相当重要的。
哈希表(HashTable)又叫作散列表,根它经过把key映射到表中一个位置来访问记录,以加快查找速度。这个映射函数就叫作散列(哈希)函数,存放记录的数组叫作散列表。
哈希表是一个时间和空间上平衡的例子。若是没有空间的限制,咱们能够直接用键来做为数组的索引,这样能够将查找时间作到最快(O(1))。若是没有时间的限制,咱们可使用无序链表进行顺序查找,这样只须要不多的内存
Hashtable是由数组与链表。数组的特色就是查找容易,插入删除困难;而链表的特色就是查找困难,可是插入删除容易。既然二者各有优缺点,那么Hashtable查找容易,插入删除也会快起来。
使用哈希函数将被查找的key转化为数组的索引。在理想的状态下,不一样的键会被转化成不一样的索引值。可是那是理想状态,咱们实践当中是不可能一直是理想状态的。当不一样的键生成了相同的索引的时候,便是哈希冲突,处理冲突方式:
LinkHashMapshi=HashMap + LinkedList
LinkedHashMap 是基于 HashMap 实现的一种集合,具备 HashMap 集合上面所说的全部特色,除了 HashMap 无序的特色,LinkedHashMap 是有序的,由于 LinkedHashMap 在 HashMap 的基础上单独维护了一个具备全部数据的双向链表,该链表保证了元素迭代的顺序。
ArrayList 是最经常使用的 List 实现类,内部是经过数组实现的,它容许对元素进行快速随机访问。
数组的缺点是每一个元素之间不能有间隔, 当数组大小不知足时须要增长存储能力,就要将已经有数
组的数据复制到新的存储空间中。
当从 ArrayList 的中间位置插入或者删除元素时,须要对数组进
行复制、移动、代价比较高。所以,它适合随机查找和遍历,不适合插入和删除。
ArrayList支持序列化功能,支持克隆(浅拷贝)功能,排序功能等
若是经过无参构造的话,初始数组容量为 0 ,当真正对数组进行添加时,才真正分配容量。每次按照 1.5 倍(位运算)的比率经过 copeOf 的方式扩容。
在 JKD6 中实现是,若是经过无参构造的话,初始数组容量为10,每次经过 copeOf 的方式扩容后容量为原来的 1.5 倍
ArrayList 的默认初始容量为 10 ,要插入大量数据的时候须要不断扩容,而扩容是很是影响性能的。所以,如今明确了 10 万条数据了,咱们能够直接在初始化的时候就设置 ArrayList 的容量!
Vector 与 ArrayList 同样,也是经过数组实现的,不一样的是它支持线程的同步,即某一时刻只有一
个线程可以写 Vector,避免多线程同时写而引发的不一致性,但实现同步须要很高的花费,所以,
访问它比访问 ArrayList 慢。
LinkedList 是用链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较
慢。另外,他还提供了 List 接口中没有定义的方法,专门用于操做表头和表尾元素,能够看成堆
栈、队列和双向队列使用
Set 注重独一无二的性质,该体系集合用于存储无序(存入和取出的顺序不必定相同)元素, 值不能重复。
对象的相等性本质是对象 hashCode 值( java 是依据对象的内存地址计算出的此序号) 判断的, 若是想要让两个不一样的对象视为相等的,就必须覆盖 Object 的 hashCode 方法和 equals 方法。
哈希表边存放的是哈希值。 HashSet 存储元素的顺序并非按照存入时的顺序(和 List 显然不一样) 而是按照哈希值来存的因此取数据也是按照哈希值取得。元素的哈希值是经过元素的hashcode 方法来获取的, HashSet 首先判断两个元素的哈希值,若是哈希值同样,接着会比较equals 方法 若是 equls 结果为 true , HashSet 就视为同一个元素。若是 equals 为 false 就不是同一个元素。
HashSet 经过 hashCode 值来肯定元素在内存中的位置。 一个 hashCode 位置上能够存放多个元素。
哈希值相同 equals 为 false 的元素是怎么存储呢,就是在一样的哈希值下顺延(能够认为哈希值相同的元素放在一个哈希桶中)。也就是哈希同样的存一列。 如图 1 表示 hashCode 值不相同的状况; 图 2 表示 hashCode 值相同,但 equals 不相同的状况。
对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。LinkedHashSet 底层使用 LinkedHashMap 来保存全部元素,它继承与 HashSet,其全部的方法操做上又与 HashSet 相同.
所以 LinkedHashSet的实现上很是简单,只提供了四个构造方法,并经过传递一个标识参数,调用父类的构造器,底层构造一个 LinkedHashMap 来实现,在相关操做上与父类 HashSet 的操做相同,直接调用父类 HashSet 的方法便可。
HashMap 和 Hashtable 都实现了 Map 接口,所以不少特性很是类似。可是,他们有如下不一样点: HashMap 容许键和值是 null,而 Hashtable 不容许键或者值是 null。
Hashtable 是同步的,而 HashMap 不是。所以, HashMap 更适合于单线程环境,而 Hashtable 适合于多线程环境。
HashMap 提供了可供应用迭代的键的集合,所以,HashMap 是快速失败的。另外一方面,Hashtable 提供了对键的列举(Enumeration)。
通常认为 Hashtable 是一个遗留的类。
下面列出了 Array 和 ArrayList 的不一样点:
Array 能够包含基本类型和对象类型,ArrayList 只能包含对象类型。
Array 大小是固定的,ArrayList 的大小是动态变化的。
ArrayList 提供了更多的方法和特性,好比:addAll(),removeAll(),iterator()等等。 对于基本类型数据,集合使用自动装箱来减小编码工做量。可是,当处理固定大小的基本数据类型的时候,这种方式相对比较慢。
ArrayList 和 LinkedList 都实现了 List 接口,他们有如下的不一样点:
ArrayList 是基于索引的数据接口,它的底层是数组。它能够以O(1)时间复杂度对元素进行随机访问。与此对应,LinkedList 是以元素列表的形式存储它的数据,每个元素都和它的前一个和后一个元素连接在一块儿,在这种状况下,查找某个元素的时间复杂度是O(n)。
相对于 ArrayList,LinkedList 的插入,添加,删除操做速度更快,由于当元素被添加到集合任意位置的时候,不须要像数组那样从新计算大小或者是更新索引。
LinkedList 比 ArrayList 更占内存,由于 LinkedList 为每个节点存储了两个引用,一个指向前一个元素,一个指向下一个元素。
也能够参考 ArrayList vs. LinkedList。
Java 提供了只包含一个 compareTo() 方法的 Comparable 接口。这个方法能够个给两个对象排序。具体来讲,它返回负数,0,正数来代表输入对象小于,等于,大于已经存在的对象。
Java 提供了包含 compare() 和 equals() 两个方法的 Comparator 接口。compare() 方法用来给两个输入参数排序,返回负数,0,正数代表第一个参数是小于,等于,大于第二个参数。equals() 方法须要一个对象做为参数,它用来决定输入参数是否和 comparator 相等。只有当输入参数也是一个 comparator 而且输入参数和当前 comparator 的排序结果是相同的时候,这个方法才返回 true。
HashSet 是由一个 hash 表来实现的,所以,它的元素是无序的。add(),remove(),contains()方法的时间复杂度是 O(1)。
另外一方面,TreeSet 是由一个树形的结构来实现的,它里面的元素是有序的。所以,add(),remove(),contains() 方法的时间复杂度是 O(logn)。
ConcurrentHashMap 是线程安全的 HashMap 的实现。主要区别以下:
JDK8 以后,ConcurrentHashMap 启用了一种全新的方式实现,利用 CAS 算法。
List、Set 是,Map 不是。Map 是键值对映射容器,与 List 和 Set 有明显的区别,而 Set 存储的零散的元素且不容许有重复元素(数学中的集合也是如此),List 是线性结构的容器,适用于按数值索引访问元素的情形。
ArrayList 和 Vector 都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增长和插入元素,它们都容许直接按序号索引娶元素,可是插入元素要涉及数组元素移动等内存操做,因此索引数据快而插入数据慢,Vector 因为使用了 synchronized 方法(线程安全),一般性能上较 ArrayList 差。
而 LinkedList 使用双向链表实现存储(将内存中零散的内存单元经过附加的引用关联起来,造成一个能够按序号索引的线性结构,这种链式存储方式与数组的连续存储方式相比,其实对内存的利用率更高),按序号索引数据须要进行前向或后向遍历,可是插入数据时只须要记录本项的先后项便可,因此插入速度较快。
Vector 属于遗留容器(早期的 JDK 中使用的容器,除此以外 Hashtable、Dictionary、BitSet、Stack、Properties 都是遗留容器),如今已经不推荐使用,可是因为 ArrayList 和 LinkedListed 都是非线程安全的,若是须要多个线程操做同一个容器,那么能够经过工具类 Collections 中的 synchronizedList 方法将其转换成线程安全的容器后再使用(这实际上是装潢模式最好的例子,将已有对象传入另外一个类的构造器中建立新的对象来增长新功能)。
参考
http://svip.iocoder.cn/Java/Collection/Interview/
https://www.jianshu.com/p/8f4f58b4b8ab
欢迎关注微信公众号【Ccww笔记】,原创技术文章第一时间推出。