java集合(网易大数据面试题)
网易大数据面试题
- 介绍一下arraylist、set、map的继承关系,画图
- 说一下ArrayList和LinkedList的底层实现原理和数据结构
- HashMap和Hashtable的区别
java集合体系
|-- Colletion(接口):实现了Iterable接口,有iterator()方法,返回一个Iterator对象
|--Set(接口):元素无序,不可重复
|--HashSet:Set接口的主要实现类;线程不安全;可以存储null
|--LinkedHashSet:遍历数据时可按照添加数据的顺序遍历;频繁遍历效率高于HashSet
|--SortedSet(接口)
|--TreeSet:可以按照添加对象的指定属性,进行排序
|--List(接口):元素有序,可重复
|--ArrayList:List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData[]存储
|--LinkedList:频繁插入删除操作,效率高;底层使用双向链表存储
|--Vector:List接口的古老实现类;线程安全,效率低;底层使用Object[] elementData[]存储
|-- Map(接口):双列数据
|--Hashtable:古老实现类;线程安全,效率低;不能存储null的key和value
|--Properties:常用来处理配置文件
|--HashMap:Map接口的主要实现类;线程不安全的,效率高;可存储null的key和value
|--LinkedHashMap:可以按照添加顺序进行遍历,HashMap底层结构基础上,添加一对指针,指向前一个后一个元素
|--SortedMap(接口)
|--TreeMap:保证按照添加的key-value对其进行排序;底层使用红黑树
HashMap的底层:数组+链表(jdk7)
:数组+链表+红黑树(jdk8)
Iterable接口与Iterator类
Iterable:接口,Collection实现了此接口,具有iterator方法,返回一个Iterator对象
Iterator:类,具有hasNext(),next()方法
对Set接口的理解
无序性不等于随机性,存储数据的顺序按照数据的哈希值决定,而不是根据索引顺序(也没有索引)
不可重复性:添加的元素按照equals()方法判断时,不能返回true
Collection源码分析
ArrayList源码分析
1.jdk7
- new一个对象时,创建了长度为10的Object[] elementData
- 扩容时,容量变为原来的1.5倍,将原有数据复制过来
2.jdk8
- new一个对象时,Object[] elementData初始化为{}
- 第一次调用add()时,底层才创建长度为10的数组,并将数据添加进去
- 后续操作无异
LinkedList源码分析
- 定义内部类Node作为保存数据的基本结构
- 定义Node类型的first和last,记录首末元素;双向链表
HashSet源码分析(底层使用HashMap)
- 底层使用数组,初始容量16,使用率超过0.75,扩大容量为原来的2倍
- 判断两个元素相等标准:通过hashcode()方法比较,继续比较equals()
- 若对象要存放在Set中,必须重写equals()和hashCode(Object obj)
- 后来的添加不进去
总结:
一次java应用中,同一个Object对象的hashcode必须保持一致,假定用于比较相等的信息没有改变(这个对象用于计算hashcode的属性)
如果两个对象的equals()返回true,那么hashcode必须一致
两者不equal,hashcode并不强制要求不相等。不过,严格不等的hashcode会提升hash表的性能。
也就是说:
当equals()重写,hashcode必须重写(默认只有在两者指向同一个Object,equals才会返回true);
用于equals()方法比较的Field,都应该用来计算hashCode值(一般由idea帮我们重写这两个函数)
添加元素的过程
- 调用hashcode()方法,计算哈希值;
- 根据哈希值通过某种算法计算出在底层数组中存放位置,如果该位置有其他元素比较哈希值,哈希值不同,添加成功;哈希值相同,调用equals方法,返回true添加失败,返回false添加成功
- 对于添加成功且原位置上有元素的情况,元素与其以链表的方式存储
- Jdk7:新元素放到数组中,指向原来的元素
- Jdk8:原来的元素在数组中,指向新元素(七上八下)
数组+链表的结构存储
LinkedHashSet源码分析
- 根据元素的hashcode来决定存储位置,同时使用双向链表维护元素的次序,使元素看起来是以插入顺序保存的
- 迭代访问时性能好
TreeSet源码分析
- 确保元素处于排序状态,两种排序方法:自然排序和定制排序。默认情况下,采用自然排序
- 底层使用红黑树结构存储数据
- 必须添加同一个类的对象
- 判断两个对象相等的唯一标准是:两个对象通过compareTo()比较返回值
自然排序vs定制排序
自然排序:元素必须实现Comparable接口,TreeSet调用元素的compareTo方法
定制排序:TreeSet构造器传入一个实现Comparator接口的实例
Map源码分析
Map结构的理解
- Map中的key:无序的,不可重复的,使用set存储所有的key(key所在的类要重写equals()和hashcode())
- Value:无序的,可重复的。使用Collection存储所有的value
- key-value构成了一个Entry对象。Map中的entry:无序的,不可重复的,使用set存储所有的entry
HashMap的底层实现原理
Jdk7 数组+链表 元素为entry
Jdk8 数组+链表+红黑树 元素为node
源码中的重要常量
DEFAULT_INITIAL_CAPACITY:默认容量16
DEFAULT_LOAD_FACTOR:默认加载因子0.75
threshold:扩容的临界值 = 容量*加载因子
TREEIFT_THRESHOLD:桶中的链表长度大于该默认值:转化为红黑树 8
MIN_TREEIFY_CAPACITYL:桶中的Node被树化时最小的hash表容量:64
- 添加元素以及扩容和之前HashSet添加元素的逻辑相同,因为HashSet底层使用了HashMap,Hashset要存储的元素作为key存储,value用Object Present代替
- 当HashMap其中一个链对象个数达到了8个,若capacity没有达到64,会先扩容。若已达到,链变为红黑树,节点类型由Node变成TreeNode
jdk8与jdk7区别
- new HashMap():底层没有创建一个长度为16的数组
- 底层数组是 ;Node[],而非Entry[]
- 首次调用put()方法,创建长度为16的数组
- 多一个红黑树;当链表数据超过8个且数组长度大于64,链表树化
LinkedHashMap
- HashMap的子类
- 在其存储结构的基础上,使用一对双向链表来记录添加元素的顺序
- 内部类:Entry
TreeMap
底层使用红黑树,类比TreeSet(自然排序和定制排序按照key)
常用方法
Collection
增:add(Object obj),addAll(Collection c)
删:clear(),remove(Object obj)
改:
查:size()有效元素个数,contains()
操作:iterator(),toArray()
List
提供额外关于索引的方法
增:add(int index,Object obj)
删:remove(int index)(注意:remove现在有两个重载方法了)
改:set(int index,Object obj)
查:get(int index)
Set
未提供额外方法
Map
增:put()
删:remove()/clear()
改:
查:containsKey(),containsValue(),size()
相关操作:keySet(),values(),entrySet()
Collections工具类
reverse()