1.什么是集合
-
集合就是一个放数据的容器,准确的说是放数据对象引用的容器
-
集合类存放的都是对象的引用,而不是对象本身
-
集合类型主要有3种:set list map
存放数据的容器,主要有三种:set list map
2.集合的特点
-
集合用于存储对象的容器,对象是用来封装数据,对象多了也需要存储集中式管理
-
和数组对比对象的大小不确定,因为集合是可变程度的
封装存储对象的容器
3.集合和数组的区别
-
数组是固定长度,集合是可变长度
-
数组存储基本数据类型,也可以存储引用数据类型,集合只能存储引用数据类型
-
数组存储的元素必须是同一个数据类型,集合存储的对象可以是不同的数据类型
数组长度固定,集合是可变长度
4.使用集合框架的好处
-
容量自增长
-
提供了高性能的数据结构和算法,使编码更轻松,提高了程序速度和质量
-
可以方便的扩展或改写集合,提高代码的复用性和可操作性
5.常用的集合类有哪些
Map接口和Collection接口是所有集合框架的父接口
collection接口中的子接口包括set接口和list接口
map接口的实现类有HashMap TreeMap HashTable(线程安全的) LinkedHashMap
Set接口的实现类 HashSet TreeSet LinkedHashSet
List接口的实现类 ArrayList LinkedList Vector(线程安全的)
6.List Set Map三者的区别?
-
java容器分为Collection和Map两大类,Collection集合的子接口有Set,List,Queue三种子接口,我们比较常用的是set,list,map接口而不是collection的子接口
-
Collection集合主要有list和set两大接口
-
list:一个有序(元素存入集合的顺序和取出的顺序一致)的容器,元素可以重复,可以插入多个null元素,元素都有索引,常用的实现类有Arraylist,linkedlist和vector
-
set:一个无序(存入和取出顺序有可能不一致的容器),不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性,set接口常用实现类是hashSet,LinkedHashSet以及TreeSet
-
-
Map是一个键值对的集合,存储键值之间的映射,key无序,唯一,value不要求有序,允许重复,map没有继承于collection接口,从map集合中检索元素时,只要给出键对象,就会返回对应的值对象,map的常用实现类是hashMap TreeMap hashTable LinkedHashMap
list一个有序容器,元素可以重复,可以插入null,常用的实现类有ArrayList LinkedList
和Vector
Set集合 无序,不可存储重复元素,只允许插入一个null元素。必须保证元素的唯一性。Set接口的实现类有HashSet LinkedHashSet TreeSet
Map是一个键值对集合,存储键值之间的映射
7.集合框架的底层数据结构
-
Collection
-
Arraylist:Object数组
-
vector:object数组
-
LinkedList:双向循环链表
-
-
Set
-
HashSet(无序唯一):基于hashmap实现,底层采用hashmap来保存元素
-
linkedHashSet:LinkedHashSet继承于HashSet,并且其内部是通过LinkedHashMap来实现的
-
TreeSet(有序唯一),红黑树(自平衡排序二叉树)
-
-
Map
-
HashMap,1.8以前,是数组+链表组成,数组是主体,链表是解决哈希碰撞冲突而存在的,拉链法来解决冲突,1.8以后再解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认8)时,将链表转化为红黑树,以减少搜索时间
-
LinkedHashMap:继承自HashMap,所以他的底层仍然是基于拉链式散列结构即由数组和链表或红黑树组成,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序,同时通过对链表进行相应的操作,实现了访问顺序的相关逻辑
-
HashTable:数组+链表组成的,数组是HashMap的主体,链表则是主要为了解决哈希冲突存在的
-
TreeMap 红黑树
-
8.哪些集合类是线程安全的
-
Vector:就比arraylist多了一个锁,因为效率低,一般不使用
-
hashtable:就比hashmap多了一个锁,线程安全,不建议使用
9.java集合快速失败机制fail-fast
-
是java集合的一种错误检测机制,当多个线程对集合进行结构上的改变的操作时,有可能会产生fail-fast机制
-
例如:假设两个线程,线程1通过迭代器在遍历集合A中的元素,在这个时候线程2修改了集合a的结构,那么这个时候程序就会抛出异常,从而产生fail-fast机制
-
原因:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个变量modcount,集合在被遍历期间如果内容发生变化,就会改变modcount的值,每当迭代器使用遍历下一个元素之前,都会看下这个count值,是的话就返回遍历,否则抛出异常,终止遍历
-
解决办法:在遍历过程中,所有涉及到改变modcount值的地方,全部加上锁
总结一下:并发多线程场景下,迭代器在每次改变值之前都会对count这个值来进行一次查询,如果有其他线程进入并改变集合的元素,这个count的值就会改变,这时候就会触发fail-fast机制,解决的办法就是通过对每个设计到改变count值的地方都可以加锁。
10.Iterator和ListIterator有什么区别
-
Iterator可以遍历set和list集合,而ListIterator只能遍历list
-
Iterator只能实现单向遍历,而ListIterator可以双向遍历
-
ListIterator实现了Iterator接口,然后添加了一些额外的功能,比如添加一个元素,替换一个元素,获取前面或后面元素的索引位置
11.遍历一个list有哪些不同的方式?每种方法的实现原理是什么?java中遍历的最佳实践是什么?
-
for循环
-
foreach
-
迭代器遍历
12.说一下ArrayList的优缺点
优点
-
底层是数组实现,是一种随机访问的模式,因此查找的时候非常快
-
在顺序添加一个元素的时候非常方便
缺点
-
删除元素的时候,需要做一次元素赋值的操作,如果要赋值的元素很多,那么就会比较耗费性能
-
插入元素的时候,也徐亚做一个元素复制操作
-
ArrayList比较适合顺序添加,随机访问场景
有序,不唯一,查询快,增删改慢
13.如何实现数组和List之间的转换
数组转List 使用Arrays.asList(array)进行转换
List转数组:使用List自带的toArray()方法
14.Arraylist和LinkedList的区别
-
数据结构实现,arraylist是动态数组数据结构实现,LinkedList是双向链表的数据结构实现
-
随机访问效率,arraylist比linkedlist在随机访问的时候效率要高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后依次查找
-
增加和删除效率:在非收尾的增加和删除操作上LinkedList效率要高
-
内存空间占用:LinkedList比ArrayList更占内存,因为双向链表除了存储数据,好要存储两个引用,一个指向前一个元素,一个指向后一个元素
-
线程安全:ArrayList和LinkedList都是不同步的,也就是不保证线程安全
-
综合来说,在需要频繁读取集合中的元素时,更推荐使用Arraylist,而在出入和删除操作较多时,更推荐使用LinkedList
-
LinkedList的双向链表也叫双链表,是链表的一种,他的每个数据节点中都有两个指针,分别指向直接后继和直接前驱,所以,从双向链表中的任意一个节点开始,都可以很方便地访问他的前驱节点和后继节点
15.ArrayList和Vector的区别
两个类都实现了list河口,他们都是有序集合。
线程安全:Vector使用了synchronized来实现线程同步,是线程安全的,而arraylist是非线程安全的
性能:arraylist在性能方面要优于vector
扩容:arraylist和vector都会根据实际的需要动态的调整容量,只不过在vector扩容每次会增加1倍,而arraylist只会增加50%
-
vector类的所有方法都是同步的,可有两个先后才能安全的访问vector对象,但是一个线程访问vector的话代码要在同步操作上耗费大量的时间
-
arraylist不是同步的,所以在不需要保证线程安全时建议使用Arraylist
16.插入数据时,arraylist linkedlist vector谁速度较快?阐述arraylist vector linkedlist的存储性能和特性?
-
Arraylist和Vector底层实现都是数组方式存储数据,数组元素数大于实际存储数据以便增加和插入元素,他们都荀彧直接按照序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢
-
Vector中的方法由于加了synchronized修饰,因此vector是线程安全容器,但性能上较arraylist差
-
linkedlist使用双向链表实现存储,按序号索引数据需要进行向前或向后遍历,但是插入数据时只需要记录当前项即可,所以Linkedlist插入速度较快
17.多线程下如何使用arraylist
-
arraylist不是线程安全的,如果遇到多线程场景,可以通过collections的synchronizedlist方法将其转换成线程安全的容器在使用
collections.synchronizedlist(arraylist)
18.list和set的区别
-
list,set都是继承自collection接口
-
list特点:一个有序容器,元素可以重复,可以插入多个null值,元素都有索引,常用的实现类有arraylist linkedlist 和vector
-
set特点:一个无序容器,不可存储重复的元素,只允许插入一个null元素,必须保证元素的唯一性,set接口的常用实现类是hashset linkedhashset 以及treeset
-
另外list支持for循环,也就是通过下标来进行遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下表来取得想要的值
-
set和list对比:set检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
-
list:和数组类似,list可以动态增长,,查找元素效率高,插入元素效率低,因为会引起其他元素位置改变
set接口
19.说一下hashset实现原理
-
hashset是基于hashmap实现的,hashset的值存放于hashmap的key上,hashmap的value统一为present,因此hashset的实现比较简单,相关hashset的操作,基本上都是直接调用底层hashmap的相关方法来完成,hashset不允许重复的值。
20.hashset如何检查重复,hashset是如何保证数据不可重复的
-
向hashset中add元素时,判断元素是否存在的依据,不仅要比较hash值,同时还要结合equales方法比较
-
hashset中的add方法会使用hashmap中的put方法
-
hashmap的key是唯一的,由源码可以看出hashset添加进去的值作为hashmap的key,并且在hashmap中如果kv相同时,会用新的v覆盖掉旧的v,然后返回旧的v,所以不会重复(hashmap比较kay是否相等是先比较hashcode再比较equals)
private static final oBJECT present=new object()private transient hashMap<E,Object> map;public HashSet(){map =new HashMap()}public boolean add(E e){return map.put(e,PRESENT)==NULL;}总结一下,hashset基于hashmap实现,hashset的add方法相当于是走了hashmap的put方法,在put的时候会先对比hash值,然后在用equals方法来比较一下值是否一致。
21.hashCode()与equals()的相关规定
-
如果两个对象相等,则hashcode一定也是相同的
- hashcode是jdk根据对象的地址或者字符串或者数字算出来的int类型的数值
-
两个对象相等,对两个equals方法返回true
-
两个对象有相同的hashcode值,他们也不一定是相等的
-
综上,equals方法被覆盖过,则hashcode方法也必须被覆盖
-
hashcode的默认行为是对堆上的对象产生独特值,如果没有重写hashcode,class的两个对象无论如何都不会相等(即时这两个对象指向相同的数据)
22. == 和equals的区别
-
==判断的是两个变量或者实例是不是指向同一个内存空间,equals是判断两个变量或者实例所指向的内存空间的值是不是相同
-
==是指内存地址进行比较equals()对字符串的内容进行比较
23.hashset和hashmap的区别
hashmap:实现了map接口,存储键值对,调用put添加元素,使用key计算hashcode,hashmap相较于hashset较快,因为他是使用唯一的键获取对象
hashset:实现set接口,仅存储对象,调用add方法向set中添加元素,hashset使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals方法用来判断对象的相等性,如果两个对象不同的话,那么返回false,hashset较hashmap慢