集合
Java集合体系结构
collection接口下:List接口(有序[存储顺序],元素可重复)和Set接口(无需,元素不可重复)
Collection接口和Map接口:Map是对Collection的补充,两个没有什么关系
Collection存储是单个值 Map存储是<K,V>键值对
List接口的实现类:ArrayList、LinkedList、Vector
Set接口的实现类:HashSet、TreeSet、LinkedHashSet
Map接口的实现类:HashMap、TreeMap、Hashtable
Collection
Collection是所有单列集合的顶层接口,在Java中存在的有序集合(List)和无序集合(Set)都从Collection接口继承,Collection中常用的方法:
boolean add(E e)
确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定集合中的所有元素添加到此集合(可选操作)。
void clear()
从此集合中删除所有元素(可选操作)。
boolean contains(Object o)
如果此集合包含指定的元素,则返回 true 。
boolean containsAll(Collection<?> c)
如果此集合包含指定 集合中的所有元素,则返回true。
boolean equals(Object o)
将指定的对象与此集合进行比较以获得相等性。
int hashCode()
返回此集合的哈希码值。
boolean isEmpty()
如果此集合不包含元素,则返回 true 。
Iterator iterator()
返回此集合中的元素的迭代器。
default Stream parallelStream()
返回可能并行的 Stream与此集合作为其来源。
boolean remove(Object o)
从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean removeAll(Collection<?> c)
删除指定集合中包含的所有此集合的元素(可选操作)。
default boolean removeIf(Predicate<? super E> filter)
删除满足给定谓词的此集合的所有元素。
boolean retainAll(Collection<?> c)
仅保留此集合中包含在指定集合中的元素(可选操作)。
int size()
返回此集合中的元素数。
default Spliterator spliterator()
创建一个Spliterator在这个集合中的元素。
default Stream stream()
返回以此集合作为源的顺序 Stream 。
Object[] toArray()
返回一个包含此集合中所有元素的数组。
T[] toArray(T[] a)
返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
由于Collection是一个顶层接口,因此对于不同类型的集合也存在两个子接口分别进行处理
- List:是一个有序的集合,并且允许重复的元素出现
- Set:是一个无序的集合,并且不允许重复元素的出现
面试题:
Collection、Collections、Connection什么区别?
Collection是所有单列集合的顶层接口;
Collections是针对集合进行处理的工具类,比如排序、查找、洗牌、逆序等操作;
Connection是Java访问数据库技术(JDBC)中的数据库连接对象的顶层接口
List接口
- 继承Collection接口
- 有序集合(存储元素的顺序和取出元素的顺序一致,可以通过索引位访问元素)
- 可重复
- 在Collection接口上新增一些方法
void add(int index, E element)
将指定的元素插入此列表中的指定位置(可选操作)。
boolean addAll(int index, Collection<? extends E> c)
将指定集合中的所有元素插入到此列表中的指定位置(可选操作)。
int indexOf(Object o)
返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。
int lastIndexOf(Object o)
返回此列表中指定元素的最后一次出现的索引,如果此列表不包含元素,则返回-1。
E remove(int index)
删除该列表中指定位置的元素(可选操作)。
E set(int index, E element)
用指定的元素(可选操作)替换此列表中指定位置的元素。
E get(int index)
返回此列表中指定位置的元素。
List subList(int fromIndex, int toIndex)
返回此列表中指定的 fromIndex (含)和 toIndex之间的视图。
List接口常见实现类
ArrayList
LinkedList
Vector
ArrayList
- 底层通过数组实现
- 数组初始容量为10(不是new就是10,在第一次执行add方法的时候)
- 内部通过数组拷贝实现扩容,当容量超出时,会扩容为原来的1.5倍
- 线程不安全(不同步)JDK1.5版本设计一个CopyOnWriteArrayList
- 数组一旦创建不能改变,因此在非末端插入和删除数据到时候,由于可能设计到数组拷贝问题,因此修改和删除效率比较低。
LinkedList
- 底层通过双向链表实现
元素是通过首尾指针连接,在进行修改(新增、删除)元素的时候只需要修改指针即可,一般情况下修改效率要比ArrayList高,但是在查询时,LinkedList比ArrayList效率低,链表从第一个元素开始依次向后搜索。 - 比较适合数据的动态插入和删除
- 随机访问和遍历速度比较慢
Vector
- 底层通过数组实现,早期集合类JDK1.0
- 线程安全(关键方法上使用synchronized修饰)
- 已淘汰-单线程环境建议使用ArrayList,多线程环境建议使用CopyOnWriteArrayList
- Vector在容量扩容时,增长为原来的2倍
Vector、LinkedList、ArrayList区别?
- Vector是老式的集合类,内部基于动态数组实现,容量扩充为原来的2倍,Vector是线程安全的实现
- ArrayList是JDK1.2之后新增的List集合的实现,内部也是基于动态数组实现,容量扩充为原来的1.5倍,ArrayList是线程不安全的实现(效率高),数据查询较快,修改较慢
- LinkedList是JDK1.2之后新增的List集合的实现,内部是基于双向链表的实现,也是线程不安全的实现,在进行数据修改方面比较快,数据查询比较慢
Set接口
- 元素的存储顺序与添加顺序无关(无序)
- 内部不允许重复元素
Set由于是一个接口,因此对于该接口,集合框架内部提供了一些常见的实现类:
- HashSet
- LinkedHashSet
- TreeSet
常用方法:
add(Object obj) 向集合中添加元素
addAll(Collection c) 将一个集合添加到当前集合中
clear() 清除集合中所有元素
isEmpty() 判断集合是否为空集合(size = 0)
remove(Object obj) 删除指定元素
iterator() 获取当前集合的迭代器对象
size() 获取集合中元素个数
HashSet
HashSet是基于哈希表的实现(内部实际就是一个HashMap),内部的元素存储根据调用元素的hashCode方法实现,由于对象的存储基于hashCode算法,因此如果多个对象的hashCode值一致,则集合中只会存储一个(在重写hashCode方法时,也必须要同时重写equals)
TreeSet
TreeSet内部实现原理是基于二叉树中的红黑树实现,使用TreeSet的前提:
- 元素必须是同一种数据类型
- 元素必须实现过Comparable接口
注意事项:
- TreeSet内部对于多个元素的去除重复根据重写的comparaTo方法来决定的,如果多个对象comparaTo方法返回值是一致的,则集合只会存储一个
- TreeSet中的元素对象的类如果未实现Comparable接口,则会抛出运行时异常
java.lang.ClassCastException
因为内部会将元素强制转换成Comparable对象
LinkedHashSet
HashSet是基于hash算法实现的元素的存储,但是由于不同的对象hash值存在差异,因此元素的存储顺序不一定按添加顺序来;在实际的开发中如果需要按照元素的添加顺序存贮,并且保证元素不重复,因此可以使用由Set集合提供的另一个实现:LinkedHashSet;
LinkedHashSet是从HashSet继承而来,内部的实现原理是基于LinkedHashMap
面试题:
HashSet,TreeSet和LinkedHashSet区别?
- HashSet是Set集合基于hash表(散列表)的实现元素存储,内部的元素存储顺序跟hash值有关,如果多个对象的hash一致(equals也一致)则集合会认为是重复元素,因此不会加入集合中;HashSet内部实现原理是基于HashMap的。
- TreeSet是Set集合基于红黑树(二叉排序树中的特殊平衡二叉树),内部元素的存储顺序是根据元素对应类实现的Comparable接口中compareTo方法进行存储,如果多个对象的comparaTo方法返回值一致,则TreeSet会认为是重复元素,因而不会重复存储;TreeSet实际就是基于TreeMap的实现
- LinkedHashSet是HashSet的子类,内部基于单链表的实现,元素的存储顺序按照添加顺序来存储,是一个有序的set集合;内部实现原理使用了LinkedHashMap。
Map
之前所学习到的List集合以及Set集合都是直接从Collection继承而来的单列子集合,另外,java的集合框架中也提供了另一种特殊的集合接口:双列集合:Map;
Map集合是由键值对结构组成,通常由一个唯一键对应一个值,集合中存储的元素键不允许重复,但是值可以重复,Map集合是一个键值对集合的顶层接口,常见的实现类主要有以下:
- HashMap
- TreeMap
- LinkeHashMap
- ConcurrentHashMap
虽然Map集合是一个键值对结构,但是实际内部存储的每一个元素都是一个Entry对象,而Entry内部包含两个属性,一个是Key,另一个是Value
常见方法:
- clear() 清除Map集合中的所有元素
- containsKey(Object key) 判断集合中是否包含指定的键
- containsValue(Object value) 判断集合中是否包含指定的值
- entrySet() 返回当前Map集合中Entry的Set集合
- get(Object key) 根据键获取值
- put(Object k,Object v) 向集合中添加元素(键值对)
- keySet() 获取键的Set集合
- remove(Object key) 根据键删除指定的元素
- size() 返回该Map集合中元素的个数
- values() 返回Map集合中所有的值集合(Collection)
HashMap
HashMap是Map中最常用的一个实现类,内部实现是基于数组+链表(JDK1.8之前;JDK1.8之后改为使用数组+链表+红黑树实现);元素的存储按照键值对的方式,存储顺序根据键的hashCode(包括equals)计算之后存储
HashMap的初始容量是16,默认的加载因子是0.75(降低hash碰撞的概率),HashMap的扩容方式为原来的2倍;实现原理:
- 在1.8之前使用的是数组和链表实现,默认情况下通过计算元素的hash值,然后和16取余(实际: hash & (length-1)),再根据计算结果将元素(Map.Entry)存储对应的数组中,如果该位置已经存在元素,则此时引入链表,采用头插法将最新的元素插入到链表头部。
- 在JDK1.8之后使用数组+链表+红黑树(平衡排序二叉树)实现,红黑树的加入有特定前提
- 数组的长度必须超过64
- 链表深度必须大于8
TreeMap
Map集合另外针对于排序的需求还有一个TreeMap的实现类,该类内部基于红黑树(平衡排序二叉树)实现;内部的元素存储顺序,由键对应的类型实现Comparable接口后,通过重写comparaTo方法实现;TreeMap的使用需要满足以下两个条件:
- key的类型必须一致
- key对应的类必须实现Comparable接口
TreeMap不允许空键出现
Hashtable
Hashtable也是键值对结构的集合解决方案,从jdk1.0之后就已经存在,从老式的集合类java.util.Dictionary继承而来,初始长度是11(HashMap是16),Hashtable是线程安全实现(HashMap是线程不安全的实现);Hashtable不允许空键值出现(HashMap允许)
LinkedHashMap
LinkedHashMap是基于链表的HashMap实现,本身也是从HashMap继承而来,通过链表实现内部元素的存储顺序保持与添加顺序一致。