1.java中的集合框架
2.介绍list和set以及map
总的来说:
List 元素是有序的、可重复;Set(集合) 元素无序的、不可重复;Map不是collection的子接口或者实现类,Map是一个接口,存放的是键值对。
分层介绍:
1.List 接口中常用类
- Vector :线程安全,但速度慢,已被 ArrayList 替代。底层数据结构是数组
- ArrayList :线程不安全,查询速度快。底层数据结构是数组
- LinkedList :线程不安全。增删速度快。底层数据结构是链表
2.Set 接口中常用的类
- HashSet :线程不安全,存取速度快,底层数据是哈希表结构。
- 它是如何保证元素唯一性的呢?依赖的是元素的 hashCode 方法和 euqals 方法。
- TreeSet :线程不安全,可以对 Set 集合中的 元素进行排序。TreeSet 底层数据结构是二叉树 。
- 它的排序是如何进行的呢?通过compareTo 或者compare方法来保证元素的有
序性。元素是以二叉树的形式存放的。
- 它的排序是如何进行的呢?通过compareTo 或者compare方法来保证元素的有
3.Map 是一个双列集合
- Hashtable :线程安全,速度慢。底层是哈希表数据结构。是 同步的。不允许 null 作为键,null 作为值。 ( 当用自定义类型对象作为 HashTable 的key 时,需要重新改写该对象的 equals()和和 hashCode() 方法来确保 y key 的唯一性 )。
- Properties :用于配置文件的定义和操作,使用频率非常高,同时键和值都
是字符串, 线程安全类。
- Properties :用于配置文件的定义和操作,使用频率非常高,同时键和值都
- HashMap :线程不安全,速度快。底层也是哈希表数据结构。是不同步的。允许 null 作为键,null 作为值。替代了 Hashtable。 ( 当用自定义类型对象作为HashMap 的 的key时,需要重新改写该对象的 equals()和 和 hashCode() 方法来确保 key的唯一性 )。
- LinkedHashMap : 可以保证 HashMap 集合有序。 存入的顺序和取出的顺序一致。(保证了顺序)
- TreeMap :可以用来对 Map 集合中的键进行排序。
3.ArrayList和LinkedList以及Vector的区别和实现原理。
1. 线程安全性
- ArrayList 不具有线程安全性,用在单线程环境中。LinkedList 也是线程不安全的,如果在并发环境下使用它们,用Colletions 类中的静态方法synchronizedList()ArrayList 和 LinkedList 进行调用即可。
- Vector 是线程安全的,即它的大部分方法都包含有关键字 synchronized。Vector 的效率没有 ArrayList 和 LinkedList 高。
2. 扩容机制
- 从内部实现机制来讲,ArrayList 和 Vector 都是使用 Objec 的数组形式来存储的,初始容量为10,当向这两种类型中增加元素的时候,若容量不够,需要进行扩容。ArrayList 扩容后的容量是之前的 1.5 倍,然后,把之前的数据拷贝到新建的数组。Vector 默认情况下扩容后的容量是之前的2倍。
- Vector 可以 设置容量增量,而 ArrayList 不可以。在 Vector 中capacityIncrement:
向量的大小大于其容量时,容量自动增加的量。
如果在创建 Vector 时,指定了capacityIncrement 的大小;则每次当 Vector 中动态数组容量需要增加时,如果 容量的增量大于零,增加的大小都是 capacityIncrement。如果容量的增量小于等于零,则每次需要增大容量时,向量的容量将增大为之前的 2 倍。 - 可变长度数组的原理:当元素个数超过数组的长度时,会产生一个新数组,将原数组的数据复制到新数组,再将新的元素添加到新数组中。
3. 增删查改的效率
- ArrayList 和 Vector 中, 从指定的位置 (用 用 index ) 检索一个对象 , 或在集合的末尾插入 、 删除一个对象的时间是一样的 ,可表示为 O(1)。但是,如果在集合的其他位置增加或移除元素那么花费的时间是 O(n)。
- LinkedList 中,在插入、删除集合中任何位置的元素所花费的时间都是一样的—O(1),但它在索引一个元素的时候比较慢 O(n)。
- 所以,如果只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector 或 ArrayList 都可以。如果是对其它 指定位置的插入,删除操作,最好选择 LinkedList。
4.Arraylist与LinkedList异同
- 是否保证线程安全:
- ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
- 底层数据结构:
- Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向循环链表数据结构;
- 插入和删除是否受元素位置的影响:
- ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E element))时间复杂度就为 O(n)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个元素都要执行向后位/向前移一位的操作。
- ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
- 是否支持快速随机访问:
- LinkedList 不支持高效的随机元素访问,而ArrayList 实现了RandmoAccess 接口,所以有随机访问功能。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
- 内存空间占用:
- ArrayList的空间浪费主要体现在在list列表的结尾会预留一定的容量空间,LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和直接前驱以及数据,构造双向链表结构)。
5.HashMap的底层实现
1.JDK1.8之前
- JDK1.8 之前 HashMap 由 数组+链表组成的(“链表散列” 即数组和链表的结合体),数组是 HashMap 的主体,链表则是主要为了解决哈希冲突而存在的。
- 如果定位到的数组位置不含链表(当前 entry 的 next 指向 null ),那么对于查找,添加等操作很快,仅需一次寻址即可;如果定位到的数组包含链表,对于添加操作,其时间复杂度依然为 O(1),因为最新的 Entry 会插入链表头部,仅需要简单改变引用链即可,而对于查找操作来讲,首先hashcode找到数组中的某一个元素,然后需要遍历链表,然后通过 key 对象的equals方法逐一比对查找.
- 所谓 “拉链法” 就是将链表和数组相结合。也就是说创建一个链表数组,数组中每一格就是一个链表entry。若遇到哈希冲突,则将冲突的值加到链表中即可。
- 结构图如下:
2.JDK1.8(提高了数据在链表中查询的时间,变为了在红黑树中查询)
- 相比于之前的版本, JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
- 结构图如下:
6.HashMap 和 Hashtable 的区别(实现同一个接口)
- 线程是否安全:
- HashMap 是非线程安全的,HashTable 是线程安全的;HashTable 内部的方法基本都经过 synchronized 修饰。(如果你要保证线程安全的话就使用 ConcurrentHashMap 吧!)
- 效率:
- 因为线程安全的问题,HashMap 要比 HashTable 效率高一点。
- 对Null key 和Null value的支持:
- HashMap 中,null 可以作为键(键的唯一性),这样的键只有一个,可以有一个或多个键所对应的值为 null;HashTable不允许空值空键,有异常抛出;
- 初始容量大小和每次扩充容量大小的不同 :
- ①创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍。
- ②创建时如果给定了容量初始值,那么 Hashtable 会直接使用你给定的大小,而 HashMap 会将其扩充为2的幂次方大小。也就是说 HashMap 总是使用2的幂作为哈希表的大小。
- 底层数据结构:
- JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间,Hashtable 没有这样的机制(和jdk1.8之前的一样)。
- 哈希值的使用不同:
- Hashtable直接使用对象的hashcode值;而hashmap会重新计算hash值,而且用&代替求模。
- 判断是否含有某个键:
- 当get方法返回null值时,既可以表示hashmap中没有该键,也可以表示该键对应的值为null,因此在hashmap中不能用get方法判断是否含有某个键,而应该用containsKey方法;
- hashtable中可以用get方法判断是否含有某个键,因为它不允许空值空键。
7.HashMap 的长度为什么是2的幂次方?
- 为了能让 HashMap 存取高效,尽量较少碰撞,也就是要尽量把数据分配均匀,每个链表/红黑树长度大致相同。这个实现就是把数据存到哪个链表/红黑树中的算法(就是数据找位置的算法)。
- 设计该算法:我们可能会想到采用%取余的操作来实现。
- 采用二进制位操作 &,相对于%能够提高运算效率。
- hash%length==hash&(length-1)的前提是 length 是2的n次方。
8.hashmap和hashset的区别
9.hashset的底层实现
hashset是作为hashmap的键存在的,他的各种方法都是通过调用hashmap的方法实现的,它对应的值是private static final Object PRESENT = new Object()。
10.集合框架底层数据结构总结
- List
- Arraylist: Object数组
- Vector: Object数组
- LinkedList: 双向循环链表
- Set
- HashSet(无序,唯一): 基于 HashMap 实现的,底层采用 HashMap 来保存元素
- LinkedHashSet: LinkedHashSet 继承于HashSet,内部是通过LinkedHashMap
- TreeSet(有序,唯一): 红黑树(自平衡的排序二叉树。)
- Map
- HashMap: JDK1.8之前HashMap由数组+链表组成的,JDK1.8是由数组和链表/红黑树组成的。
- LinkedHashMap: LinkedHashMap 继承自 HashMap,所以它的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成。另外,LinkedHashMap 在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。
- HashTable: 数组+链表
- TreeMap: 红黑树(自平衡的排序二叉树)