Collection体系集合
1.ArrayList和Vector的区别。
这两个类都是List接口下的实现类,而List下总共有三个实现类,分别是ArrayList、LinkedList、Vector。List用于存放多个元素,其中的元素是由于可重复的。
ArrayList
是List中最常用的实现类,内部是由数组实现的,而数组的缺点是每个元素之间不能有间隔,当数组的存储空间不足时就需要扩容,数组扩时就需要将元素组中的数据复制到新的存储空间中。当ArrayList中需要插入或删除元素时需要将原数组进行复制、移动的操作代价比较高。因此ArrayList适合于查找和遍历,不适合于插入和删除操作.vector
与ArrayList相同的时他们的底层都是由数组实现的,不同的是vector
是线程同步的,就是某一时间内只能有一个线程对vector进行操作。这样就可以避免多线程操作vector
导致的数据不一致。但是会降低效率。LinkedList
底层是由链表结构存储数据的。适合于插入和删除操作。但是不适合于查询和遍历操作。
2.说说ArrayList Vector,LinkedList的存储性能和特性。
ArrarList
和vector
的底层都是由数组实现的其存储时必须存储在一片连续的空间上,数组的优势在于其查询的速度快,但是当数组的存储空间不足时就涉及到数组的扩容,数组扩容需要将原有的数据复制到新的存储空间中。插入和删除的操作需要对数组进行复制和移动的操作代价较高。vector
和ArrayList
的区别在于Vector
时线程安全的。
3.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
快速失败
Iterator在遍历一个集合对象的时候,如果集合对象进行了修改(增、删、改)操作那么皆会抛出Concurrent Modification Exception
异常;
- 原理:Iterator在遍历的过程中会有一个
modcount
的变量,集合在修改操作的过程中就会修改modcount
的值,在集合进行遍历的时候会有一个expectedmodCount
的值当在迭代的过程中如果发现modeCount
的值和expectedmodCount
的值不相同的时候就会抛出Concurrent Modification Exception
异常- 注意:java.util包下的集合类都是快速失败的。不能在多线程并发下修改
安全失败
采用安全失败的集合不是在原集合内容上进行操作的,而是先拷贝原集合,在拷贝的集合上进行操作的。
- 原理:在拷贝的集合上进行操作就不会导致
modCount
和exceptedModCount
的值不同,因此不会触发Concurrent Modification Exception
。- 缺点:安全失败避免了
Concurrent Modification Exception
异常,但是迭代器是在拷贝的集合上进行操作的,所以迭代其实无法发访问到修改后的内容。即:迭代器遍历的是开始遍历那一刻拿到的集合拷贝,在遍历期间原集合发生的修改迭代器是不知道的- 注意:java.util.concurrent包下的集合都是安全失败的。可以在多线程并发下进行修改操作
4.hashmap 的数据结构。
hashmap的数据结构为:
数组
+链表
+红黑树(jdk1.8添加的)
hasnmap进行存储值时会先会根据key值的hash%数组的长度值计算出key值在数组中存储位置的下标,将其value值封装成一个包含hash值,key值,value值和next的实体存储在对应的数组位置后的链表中。当同一链表上的长度过长时(长度大于8时)链表就会转换为红黑树,以提高查找的效率。
5.HashMap的工作原理是什么?
hashmap是map的一个实现类,故其是以key-value进行存储的,但是hashmap的底层是由数组+链表+红黑树实现存储的。存储时会先根据key值的hash值进行对数组长度取余运算,计算出其在hash表中的存储位置,再将其key-value和下一个节点封装起来存储到链表中。当链表的长度大于8的时候就会将链表转换为红黑树以提高其查询效率。
6.Hashmap什么时候进行扩容呢?
hashmap的扩容发生在其数组中存储的数据=数组的长度*loadFactor(上座率)时。loadFactor的默认值为0.75。hashmap扩容一次扩容为原来的两倍。因为其扩容的时候所有的元素的hash值对数组长度取余后都可能会不同,所以所有的元素都要重新计算存储位置进行存储。这一过程对程序来说是非常漫长的,所以我们在使用hashmap的时候尽量在创建之初就计算好我们的一个大概的长度(长度要考虑到长度乘以loadFactor)尽量避免在程序运行过程中hashmap扩容这一操作。
7.List、Map、Set三个接口,存取元素时,各有什么特点?
单独这个问题提问时可以简述其三个接口的底层原理
存值时:
- List以特定的索引(有顺序的存放)来存放元素,可以有重复的元素
- map保存键值对的映射,映射关系可以是一对一或多对一,键值是无序的不可重复的,值可以重复
- set存放的元素是无序的不可重复的
取值时:
- List可以通过基本for循环,增强for循环以及迭代器iterator来取值
- map在取值时需要先将其所有的key值拿到转换为set在通过迭代器进行取值
- set通过迭代器进行取值
8.Set里的元素是不能重复的,那么用什么方法来区分重复与否呢?是用==还是equals?它们有何区别?
是通过equals来区分是否相同的,因为如果set集合中有两个元素是同一个类,其内容相同但是指向的地址不同如果该类重写了equals方法则第二次add进去的元素会覆盖掉第一次的元素,
==比较的是两个对象的地址是否相同
equals比较的是两个对想的内容是否相同
9.两个对象值相同(x.equals(y) = = true),但却可有不同的hash code,这句话对不对?
对的,当一个类在重写equals方法的时候没有对hashcode进行重写那么会出现equals结果为true但是hashcode不同的情况
10.heap和stack有什么区别。
11.Java 集合类框架的基本接口有哪些?
- collection接口
- List接口
- ArrayList实现类
- LinkedList实现类
- Vactor实现类
- Set接口
- SortedSet接口
- TreeSet实现类
- HashSet实现类
- EnumSet实现类
- Queue接口
- Map接口
- SortedMap接口
- TreeMap实现类
- HashMap实现类
12.HashSet和TreeSet有什么区别?
HashSet
元素是无序的,TreeSet
实现了SortedSet
对元素进行自然排序Hashset必须要重写hashcode方法,相同的String的hashcode是相同的所有不允许存入相同的String类型,HashSet允许元素为null值,但是只允许一个。
TreeSet是二叉树实现的,Tree中的数据是自动排好序的不允许放入null值
13.HashSet的底层实现是什么?
HashSet的底层是基于Hashmap实现的使用的hashmap的方式存储所有数据的,在hashSet中所有的key全部存在Hsahmap的key值上 value是一个统一的值
14.LinkedHashMap的实现原理?
在世用hashMap的时候可能会与带按插入的顺序来取值,但是HashMap在存储的时候是按照KEY的hash值对数组的长度取余来确定存储位置的,是不具备这一功能的,所有由LinkedHshMap维护的顺序有两种,插入顺序和访问顺序,LinkedHashMap底层是通过双链表结构来维护节点的顺序的,默认是按插入顺序维护的,每个节点都进行了双向连接。head指向插入的第一个节点,tail指向最后一个节点
15.为什么集合类没有实现Cloneable和Serializable接口?
克隆(cloning)或者是序列化(serialization)的语义和含义是跟具体的实现相关的。因此,应该由集合类的具体实现来决定如何被克隆或者是序列化。
克隆分为深拷贝和浅拷贝, 具体的拷贝实现需要用户自己根据对象的特性来实现, 集合类又是一个容器, 会装载不同的对象(各种各样的). 不可能每个对象都实现克隆,
序列化也是同样的道理。
虽然List没有继承序列化和克隆接口,但是它的子类ArrayList都继承了这两个接口。 并且根据自己的特性对这两个接口的实现都进行了自己的优化
16.什么是迭代器(Iterator) ?
Iterator 是可以遍历集合的对象,为各种容器提供了公共的操作接口,隔离对容器的遍历操作和底层实现,从而解耦。
缺点是增加新的集合类需要对应增加新的迭代器类,迭代器类与集合类成对增加。
17.Iterator和Listlterator的区别是什么?
iterator用于遍历所有类型的集合
ListIterator只能用来遍历List类型的集合
18.数组(Array)和列表(ArrayList)有什么区别?什么时候应该使用Array而不是ArrayList ?
数组Array是在初始化的时候固定长度的,而ArrayList初始化时没有给定长度的话会有默认长度。
场景:事先已经预知需要存储的长度时使用array,长度不确定时使用ArrayList
19.Java集合类框架的最佳实践有哪些?
1.根据应用需要正确选择要使用的集合类型对性能非常重要,比如:假如知道元素的大小是固定的,那么选用Array类型而不是ArrayList类型更为合适。
2.有些集合类型允许指定初始容量。因此,如果我们能估计出存储的元素的数目,我们可以指定初始容量来避免重新计算hash值或者扩容等。
3.为了类型安全、可读性和健壮性等原因总是要使用泛型。同时,使用泛型还可以避免运行时的ClassCastException。
4.使用JDK提供的不变类(immutable class)作为Map的键可以避免为我们自己的类实现hashCode()和equals()方法。
5.编程的时候接口优于实现
6.底层的集合实际上是空的情况下,返回为长度是0的集合或数组而不是null。
20.Comparable和Comparator接口是干什么的?列出它们的区别
Comparable和Comparator都是用来对引用类型进行自定义的比较的。
Comparable是定义在类的内部的类实现Comparable接口重写其中的compare方法自定义排序规则
Comparator可以定义在类的外部实现ComparaTo方法自定已排序规则即可。
21.Collection和 Collections的区别。
Collection是一个接口
Comparable是定义在类的内部的类实现Comparable接口重写其中的compare方法自定义排序规则
Comparator可以定义在类的外部实现ComparaTo方法自定已排序规则即可。
21.Collection和 Collections的区别。
Collection是一个接口
Collections为Collection接口的实现了提供了相应的一些方法例如:sort() 自动将集合中元素排序(默认升序),集合中的对象要具备排序能力 shuffle() 打乱数组中元素 reverse() 将集合中元素倒置