题1:Java 中常用的集合有哪些?
Map接口和Collection接口是所有集合框架的父接口
Collection接口的子接口包括:Set接口和List接口。
Set中不能包含重复的元素。List是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。
Map接口的实现类主要有:HashMap、Hashtable、ConcurrentHashMap以及TreeMap等。Map不能包含重复的key,但是可以包含相同的value。根据键得到值,对map集合遍历时先得到键的set集合,对set集合进行遍历,得到相应的值。
Set接口的实现类主要有:HashSet、TreeSet、LinkedHashSet等
List接口的实现类主要有:ArrayList、LinkedList、Stack以及Vector等
Iterator所有的集合类,都实现了Iterator接口,这是一个用于遍历集合中元素的接口,主要包含以下三种方法:
hasNext()是否还有下一个元素
next()返回下一个元素
remove()删除当前元素
题2:为什么 Map 接口不继承 Collection 接口?
1)Map提供的是键值对映射(即Key和value的映射),而Collection提供的是一组数据并不是键值对映射。
2)若果Map继承了Collection接口,那么所实现的Map接口的类到底是用Map键值对映射数据还是用Collection的一组数据呢?比如平常所用的hashMap、hashTable、treeMap等都是键值对,所以它继承Collection是完全没意义,而且Map如果继承Collection接口的话,违反了面向对象的接口分离原则。
接口分离原则:客户端不应该依赖它不需要的接口。
另一种定义是类间的依赖关系应该建立在最小的接口上。
接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。
接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署,让客户端依赖的接口尽可能地小。
3)Map和List、Set不同,Map放的是键值对,List、Set存放的是一个个的对象。说到底是因为数据结构不同,数据结构不同,操作就不一样,所以接口是分开的,还是接口分离原则。题3:Collection 和 Collections 有什么区java.util.Collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式。其直接继承接口有List与Set。
Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set
java.util.Collections是一个包装类。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,服务于Java的Collection框架。
题4:ArrayList 和 LinkedList 有什么区别?
1)ArrayList是Array动态数组的数据结构,LinkedList是Link链表的数据结构,此外,它们两个都是对List接口的实现。前者是数组队列,相当于动态数组;后者为双向链表结构,也可当作堆栈、队列、双端队列。
2)当随机访问List时(get和set操作),ArrayList比LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找。
3)当对数据进行增加和删除的操作时(add和remove操作),LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后所有数据的下标索引造成影响,需要进行数据的移动。
4)从利用效率来看,ArrayList自由性较低,因为它需要手动设置固定大小的容量,但是它的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用;而LinkedList自由性较高,能够动态的随数据量的变化而变化,但是它不便于使用。
5)ArrayList主要控件开销在于需要在List列表预留一定空间;而LinkList主要控件开销在于需要存储结点信息以及结点指针信息。
题5:HashMap 和 HashTable 有什么区别?
Hashtable是线程安全,而HashMap则非线程安全。
Hashtable所有实现方法添加了synchronized关键字来确保线程同步,因此相对而言HashMap性能会高一些,平时使用时若无特殊需求建议使用HashMap,在多线程环境下若使用HashMap需要使用Collections.synchronizedMap()方法来获取一个线程安全的集合。
HashMap允许使用null作为key,不过建议还是尽量避免使用null作为key。HashMap以null作为key时,总是存储在table数组的第一个节点上。而Hashtable则不允许null作为key。
HashMap继承了AbstractMap,HashTable继承Dictionary抽象类,两者均实现Map接口。
HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
HashMap扩容时是当前容量翻倍即:capacity*2,Hashtable扩容时是容量翻倍+1即:capacity*2+1。
HashMap和Hashtable的底层实现都是数组+链表结构实现。
题6:HashMap 是怎么扩容的?
当HashMap中元素个数超过数组大小*loadFactor时,需进行数组扩容。
loadFactor默认值为0.75,默认情况下,数组大小为16,HashMap中元素个数超过16 * 0.75=12的时,就把数组的大小扩展为2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常消耗性能的操作,所以能够预选知道HashMap中元素的个数,应该预设数组的大小,可以有效的提高HashMap的性能。
假设有1000个元素new HashMap(1000),理论上来讲new HashMap(1024)更合适,不过上面已经提过即使是1000个元素,HashMap也会自动设置为1024。但是new HashMap(1024),而0.75*1024 <1000, 为了可以0.75 * size >1000,必须new HashMap(2048),避免了resize的问题。
总结:
添加元素时会检查容器当前元素个数。当HashMap的容量值超过临界值(默认16 * 0.75=12)时扩容。HashMap将会重新扩容到下一个2的指数幂(16->32->64)。调用resize方法,定义长度为新长度(32)的数组,然后对原数组数据进行再Hash。注意的是这个过程比较损耗性能。
题7:JDK1.8 和 JDK1.7 中 ArrayList 的初始容量多少?
JDK1.7下ArrayList()初始化后的默认长度是10,源码如下:
//无参构造方法
public ArrayList() {
this(10);
}
public ArrayList(int initialCapacity) {
super();
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
this.elementData = new Object[initialCapacity];
}
通过上述源代码可以看出,默认的构造方法中直接指定数组长度为10,同时调用重载的构造方法,创建了长度为10的一个数组。
题8:JDK1.8下ArrayList()初始化后的默认长度是0,源码如下:
//无参构造方法
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
构造方法中静态类型的数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}是个空数组,数组长度为0,因此JDK1.8下的ArrayList()初始化后默认的数组长度为0。
题8:Arrays.asList() 有什么使用限制?
1)Arrays.asList()方法不适用于基本数据类型
byte
short
int
long
float
double
boolean
2)Arrays.asList()方法把数组与列表链接起来,当更新其中之一时,另一个自动更新。
3)Arrays.asList()方法不支持add和remove方法。
题9:Set 为什么是无序的?
Set系列集合添加元素无序的根本原因是底层采用哈希表存储元素。
JDK1.8以下版本:哈希表 = 数组 + 链表 + (哈希算法)
JDK1.8及以上版本:哈希表 = 数组 + 链表 + 红黑树 + (哈希算法)
当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。
题10:Comparable 和 Comparator有什么区别?
Comparable接口出自java.lang包,它有一个compareTo(Object obj)方法用来排序。
Comparator接口出自java.util包,它有一个compare(Object obj1, Object obj2)方
法用来排序。
一般对集合使用自定义排序时,需要重写compareTo()方法或compare()方法。
当需要对某一个集合实现两种排序方式,比如一个用户对象中的姓名和身份证分别采用一种排序方法。
方式一:重写compareTo()方法实现姓名、身份证排序
方式二:使用自定义的Comparator方法实现姓名、身份证排序
方法三:使用两个Comparator来实现姓名、身份证排序
其中方式二代表只能使用两个参数的形式Collections.sort()。
Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式:
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}