Java面试题-集合泛型
Java面试题-集合泛型
-
ArrayList和LinkedList区别?
List是一个有序的集合,可以包含重复的元素,它继承自Collection,其中ArrayList和LinkedList是List的两个重要的实现类。
ArrayList是基于索引,可以随机存取的数据结构,获取数据的时间复杂度是O(1),但删除数据需要重排数组中的所有数据,因此删除数据时间开销大。ArrayList的底层实现是Array,数组扩容可以自动实现。
LinkList的数据结构是一个双链表,在添加和删除元素时具有比ArrayList更好的性能,但在查询和修改元素方面弱于ArrayList。
-
HashMap和HashTable的区别?
-
对null的支持不同:
HashTable的键值均不能为空;HashMap的值可以为空,键最多有一个为空。
-
对外提供的接口不同:
HashTable比HashMap多提供了elements()和contains()两个方法。elements()用于返回此HashTable中value的枚举,contains()用于此HashTable是否包含传入的value,作用与containsValue()一致。
-
父类不同:
HashMap集成自AbstractMap类,HashTable继承自Dictionary类。
-
安全性不同:
HashMap线程不安全,HashTable线程安全。另:ConcurrentHashMap是线程安全的,效率比HashTable高很多倍。
-
初始容量大小和每次扩容容量大小不同。
-
计算hash值的方法不同。
-
-
Collection和Collections的区别?
Collection是集合类的上级接口,子接口有Set,List,LinkedList,ArrayList,Vector,Stack,Set;
Collections是集合类的一个帮助类,包含很多有关集合操作的方法,像是一个工具类为Collection框架服务。
-
泛型的作用?
用于约束集合中存放数据的类型。
-
List,Set,Map的区别?
-
List(对付顺序的好帮手):List接口存储一组不唯一,有序的对象。
-
Set(注重独一无二的性质):不允许重复的集合。不会有多个元素引用相同的对象。
-
Map(用Key来检索专有的Value):使用键值对进行存取。Map会维护与Key有关联的值。两个Key可以引用相同的对象,但Key不能重复。Value可以是任意值或对象。
-
-
Array与ArrayList的区别?
Array和ArrayList都是用来存储数据的集合。可以认为ArrayList是Array的升级版,ArrayList对底层的数组进行了封装和功能扩展,拥有许多原生数组没有的一些功能。
-
Map的特点
键值对存取数据,元素存储无序,不允许出现重复键。
-
集合类存放与Java.util包中,主要有哪几种接口
- Collection:集合基本接口
- Iterator:迭代器,可以通过迭代器遍历集合中的数据。
- Map:映射表的基础接口
-
什么是List接口?
List是有序的集合Collection,一共有三个List实现类,分别是ArrayList、LinkedList、Vector。
-
说说ArrayList(数组)
ArrayList是最常用的List实现类,内部是通过数组实现的,允许对元素进行随机访问。缺点是每个元素直接不能有间隔,当数组空间不足时需要将已有的数组数据复制到新的存储空间中。当从ArrayList插入删除元素是,需要对数组中大部分元素进行移动,代价比较高。因此它适合随机查找和遍历,不适合插入和删除。
-
说是Vector(数组实现,线程同步)
Vector底层通过数组实现,它支持线程同步,访问速度较慢。
-
说是LinkedList(链表)
LinkedList用链表结构存取数据,适合数据添加和删除,但不支持随机访问,因此查询和遍历效率低。它提供了专门操作链头和链尾的方法,约束后可以当做栈、队列、堆来使用。
-
什么是Set集合?
Set注重独一无二的性质,用于存取无序元素,值不能重复。判断对象是否相等的本质通过hashCode的值。
-
什么是HashSet?(哈希表)
哈希表存放的是哈希值。HashSet存储元素的顺序是按照哈希值来存的,因此取数据也是根据哈希值来取。元素的哈希值是通过元素的hashCode方法来获取的。HashSet通过hashCode值来确定元素在内存中的位置。
-
什么是TreeSet?(二叉树)
- TreeSet()是使用二叉树的原理对新add()的对象按指定的顺序排序。
- Integer和String对象支持默认的TreeSet排序,自定义对象若想排序需要实现Comparable接口,并覆写相应的compareTo函数。
-
说是LinkedHashSet(HashSet+LinkedHashMap)
对LinkedHashSet而言,它继承与HashSet、又基于LinkedHashMap来实现的。LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同,因此LinkedHashSet的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数,调用父类的构造器,底层构造一个LinkedHashMap来实现,在相关操作上与父类HashSet的操作相同,直接调用父类的HashSet方法即可。
-
HashMap(数组+链表+红黑树)
HashMap根据键的hashCode值存储数据,大多数情况可以直接定位到它的值,因此访问速度快,但遍历的顺序不确定。HashMap最多只允许一条记录的键为null,允许多条记录的值为null。HashMap非线程安全,即任一时刻可以有多个线程写HashMap,可能会导致数据的不一致。如果需要满足线程安全,可以用Collections的synchronizedMap方法使HashMap具有线程安全的能力,或者使用ConcurrentHashMap。
Java1.7及以前的HashMap:底层由数组+链表组成。数组中每个元素是一个单向链表。
Java1.8及以后的HashMap:底层由数组+链表+红黑树组成。当链表元素小于等于8是存储元素方式与Java1.7相同,时间复杂度为O(n);当链表中元素超过8时会将链表转换为红黑树,可以降低时间复杂度为O(logN)。
-
说说ConcurrentHashMap
-
Segment段:
ConcurrentHashMap和HashMap思路差不多,但它额外支持并发操作。整个ConcurrentHashMap由一个个Segment组成,Segment代表“部分”或“一段”的意思,所以很多地方都会讲其描述为分段锁。
-
线程安全(Segment继承ReentrantLock加锁)
简单理解就是,ConcurrentHashMap是一个Segment数组,Segment通过继承ReentrantLock来进行加锁,所以每次需要加锁的操作锁住的一个segment,这样只要保证每个Segment是线程安全的,也就实现了全局的线程安全。
-
concurrentLevel:并行级别、并发数、Segment数,默认为16。
-
Java8实现:类似HashMap,也引入了红黑树。
-
-
HashTable(线程安全)
HashTable是遗留类,很多映射的常用功能与HashMap类似,不同的是它继承自Dictionary类,并且是线程安全的既任一时间只能由一个线程写HashTable,并发性不如ConcurrentHashMap,因为ConcurrentHashMap引入了分段锁。
-
TreeMap(可排序)
TreeMap实现SortedMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以排序指定的比较器,当Iterator迭代器变量TreeMap时,得到的记录是排过序的。如果使用排序的映射,建议使用TreeMap。在使用TreeMap时,key必须实现Compareable接口或者在构造TreeMap传入自定义的Comparator,否则运行时会抛出java.lang.ClassCastException类型异常。
-
LinkedHashMap(记录插入顺序)
LinkedHashMap是HashMap的一个子类,保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是插入的。也可以在构造时带参数,按照访问次序排序。
-
泛型类
和泛型方法一样,泛型类的类型参数声明包含一个或多个类型参数,参数用逗号隔开。一个泛型参数,也被称为一个类型变量,是用于指定一个泛型类型名称的标识符。
-
类型通配符?
类型通配符一般是用?代替具体的类型参数。
-
类型擦除
Java中的泛型基本是在编译器这个层次实现的,在生成的Java字节代码中是不包含泛型中的类型信息的。使用泛型的时候加上的类型参数,会被编译器在编译的时候去掉,这个过程就被称为类型擦除。由泛型附加的类型信息对JVM来说是不可见的。