集合
什么是集合?
集合是用来存储不同数据类型的容器。
集合的特点
- 对象封装数据,对象多了也需要存储。集合用于存储对象。
- 对象的个数确定可以使用数组,对象的个数不确定的可以使用集合。因为集合的长度是可以改变的。
集合与数组的区别
- 数组的长度是固定的;集合的长度是可变的。
- 数组可以存储基本数据类型,也可以存储引用数据类型;集合只能存储引用数据类型。
- 数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同的数据类型。
常用的集合类有哪些?
Map接口和Collection接口是所有集合框架的父接口:
- Collection接口的子接口包括:Set接口和List接口;
- Map接口的实现类主要由:HashMap,TreeMap,Hashtable,ConcurrentHashMap等;
- Set接口的实现类主要有:HashSet,TreeSet,LinkedHashSet等;
- List接口的实现类主要有:ArrayList,LinkedList,Stack以及Vector等;
List,Set,Map三者的区别?List,Set,Map是否继承自Collection接口?List,Map,Set三个接口存取元素时,各有什么特点?
Java容器分为Collection和Map两大类,Collection集合的子接口有Set,List,Queue三种子接口。我们比较常用的是Set,List。Map接口不是collection的子接口。
Collection集合主要有List和Set两大接口
List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的类有ArraysList,LinkedList和Vector。
Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素的唯一性。Set接口的常用实现类是HashSet,LinkedHashSet以及TreeSet。
Map是一个键值对集合,存储键,值和之间的映射。Key无序,唯一;value不要求有序,允许重复。Map没有继承与Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map的常用实现类有:HashMap,TreeMap,HashTable,LinkedHashMap,ConcurrentHashMap。
集合框架底层数组数据结构
Collection
1.List
- ArraysList :Object数组
- Vector:Object数组
- LinkedList:双向循环链表
2.Set
- HashSet(无序,唯一):基于HashMap实现的,底层采用HashMap来保存元素
- LinkedHashSet:LinkedHashSet继承于HashSet,并且其内部是通过LinkedHashMap来实现的。
- TreeSet(有序,唯一):红黑树(自平衡的排序二叉树)
Map
- HashMap:JDK1.8之前HashMap由数组+链表组成的,数组是Hashmap的主体,链表则是主要为了解决哈希冲突而存在的。JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认8)时,将链表转化为红黑树,以减少搜索时间
- LinkedHashMap:LinkedHashMap继承于HashMap。
- HashTable:数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突而存在的
- TreeMap:红黑树(自平衡的排序二叉树)
那些集合是线程安全的?
- vector:就比arraylist多了个同步化机制(线程安全),效率低,不建议使用。
- statck:堆栈类,先进后出
- hashtable:就比hashmap多了个线程安全
- enumeration:枚举,相当于迭代器
遍历一个List有哪些种不同的方式?
- for循环遍历:基于计数器。在集合外部维护一个计数器,然后依次读取每一个位置上的元素,当读取到最后一个元素后停止。
- 迭代器便利:Iterator;
- foreach循环遍历。
ArrayList的优缺点
优点:底层是以数组实现,是一种随机访问模式。查找速度非常快。
在顺序添加一个元素的时候非常方便
缺点:删除元素的时候,需要做一次元素复制操作。如果要复制元素很多,那么就会比较耗费性能
插入元素的时候,也需要做一次元素复制操作。
ArrayList比较适合顺序添加,随机访问的场景。
如何实现数组和List之间的转换?
数组转List:使用Arrays.asList(array)进行转换。
List转数组:使用List自带的toArray()方法。
ArrayList和LinkedList的区别是什么?
- 数据结构实现:ArrayList是动态数组的数据结构实现,而LinkedList是双向链表的数据结构实现。
- 随机访问效率:ArrayList比LinkedList在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以移动指针从前往后依次查询。
- 增加和删除的效率:在非首尾的增加和删除操作,LinkedList要比ArrayList效率要高,因为ArrayList增删操作要影响数组内的其他数据的下标。
- 内存空间占用:LinkedList比ArrayList更占用内存,因为LinkedList的节点除了存储数据,还存储了两个引用,一个指向前一个元素,一个指向后一个元素。
- 线程安全:ArrayList和LinkedList都是不同步的,也就是线程不安全的。
总结:在需要频繁读取数据集合中的元素时,更推荐使用ArrayList,而在插入和删除操作较多时,更推荐使用LinkedList。
List和Set的区别
List,set都是继承自Collection接口
List特点:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的类由ArrayList,LinkedList和Vector。
Set特点:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复的元素,只允许存入一个null元素,必须保证元素的唯一性。Set接口常用的实现类是HashSet,LinkedHashSet以及TreeSet。
另外List支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为它无序,无法用下标来取得想要的值。
总结:
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组相同,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
Set接口
实现原理
HashSet是基于HashMap实现的,HashSet的值存放于HashMap的key上,HashMap的value统一为Present,因此HashSet的实现比较简单,相关HashSet的操作,基本上是调用底层HashMap的相关方法来完成,HashSet不允许重复的值。
HashSet如何健康检查重复?HashSet是如何保证数据不可重复的?
向HashSet中的add()元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equles方法比较。
HashSet中的add()方法会使用HashMap的put()方法。
HashMap的key时唯一的,由源码可以看出HashSet添加进去的值就是作为HashMap的key,并且在HashMap中如果K/V相同时,会用新的V覆盖掉旧的V,然后返回旧的V。所以不会重复(HashMap比较key是否相等是先比较hashcode再比较equals)。
HashCode()和equals()的相关规定
- 如果两个对象相等,则hashcode一定也是相等的
- 两个对象相等,对两个equals方法返回true
- 两个对象有相等的hashcode值,他们也不一定是相等的
- 综上,equals方法被覆盖,则hashCode方法也必须重写
- hashcode()的默认行为是对堆上的对象产生独特值。如果没有重谢hashCode(),则该class的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。
HashMap是如何解决哈希冲突的?
Hash:哈希,一般翻译为散列,就是把任意长度的输入通过散列算法,变换成固定长度的散列值(哈希值)。
什么是哈希冲突?
当两个不同的输入值,根据统一单列函数计算出相同的散列值的现象,我们旧把它叫做碰撞(哈希碰撞)。
HashMap的数据结构
在Java中,保存数据有两种比较简单的数据结构:数组和链表。
数组的特点:寻址容易,插入和删除困难;
链表的特点:寻址困难,但插入和删除容易;
所以我们将数组和链表结合在一起,发挥两者各自的优势,使用一种叫做链地址法的方式可以解决哈希冲突。
如何使用Object作为HashMap的Key,应该怎么办呢?
重写hashCode和equals方法。