集合类的特点
a. 只能存储引用数据类型:因为泛型只能传入引用的数据类型
b. 可以自动地调整自己的大小
数组和集合类都是容器,它们有何不同?
a. 数组可以存储基本数据类型的数据,集合不可以。
b. 数组的长度是固定的,集合可以自动调整自己的大小。
c. 数组的效率高,相对来说集合效率比较低。
d. 数组没有API,集合有丰富的API。
集合类特点的回答顺序:
1, 是谁的子类, 描述了什么数据结构
2, 底层结构: 数组, 链表, 数组+链表, 数组(默认的初始容量, 扩容机制)
3, 有序, 重复, null 问题
4, 线程安全与否
// 1, Collecion是Collection集合体系的顶级接口
// 2, Collection有些子实现有序, 有些子实现无序
// 3, Collection有些子实现允许存储重复元素, 有些子实现不允许存储重复元素
// 4, Collection有些子实现允许存储null, 有些子实现不允许存储null
boolean add(E e)
确保此 collection 包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c)
将指定 collection 中的所有元素都添加到此 collection 中(可选操作)。
void clear()
移除此 collection 中的所有元素(可选操作)。
boolean contains(Object o)
如果此 collection 包含指定的元素,则返回 true。
boolean containsAll(Collection<?> c)
如果此 collection 包含指定 collection 中的所有元素,则返回 true。
boolean equals(Object o)
比较此 collection 与指定对象是否相等。
int hashCode()
返回此 collection 的哈希码值。
boolean isEmpty()
如果此 collection 不包含元素,则返回 true。
Iterator<E> iterator()
返回在此 collection 的元素上进行迭代的迭代器。
boolean remove(Object o)
从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。
boolean removeAll(Collection<?> c)
移除此 collection 中那些也包含在指定 collection 中的所有元素(可选操作)。
boolean retainAll(Collection<?> c)
仅保留此 collection 中那些也包含在指定 collection 的元素(可选操作)。
int size()
返回此 collection 中的元素数。
Object[] toArray()
返回包含此 collection 中所有元素的数组。
<T> T[] toArray(T[] a)
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。
<T> T[] toArray(T[] a)
// 注意: toArray方法虽然是一个泛型方法, 可以接受一个泛型类型的数组, 但是并不意味着可以接受任何类型(因为在实际运行的时候如果这个给的数组存储不了会报错)
// 如果给定的数组长度足够, 泛型的toArray方法, 参数和返回的数组是同一个数组
// 如果给定的数组长度不够, 返回的数组和参数数组没有关系, 是一个新数组, 参数数组仅仅起到类型推断的作用
// 如果我们给定的数组足够长, 并且collection中存储了n个元素, 那么数组中0-n-1位置存储collection的元素, 下标为n位置置位null, 之后位置不操作, 是数组原来存储的内容
boolean hasNext()
如果仍有元素可以迭代,则返回 true。
E next()
返回迭代的下一个元素。
void remove()
从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。
在面试回答的时候,iterator一开始指向的是第一个元素的前一个位置,在调用next后指向的是第一个元素和第二个元素之间,这个时候输出的就是刚才跳过去的那个数。但实际的代码并不是这样实现的
// 注意1: remove方法
// remove方法删除的是刚刚遍历过得元素, 注意是在源数据上删除
// 并且, remove不能在遍历之前进行, 也不能做连续的删除
// 这个删除操作更适用的场景是对某个元素遍历之后, 这个元素不符合预期我们才把他删除, 实际上对于iterator迭代来说, 重要是遍历而非删除
// 注意2: iterator遍历和操作数据, 操作的都是源数据, 并没有产生新的数据
// 注意3: 在iterator遍历的过程中, 为了保证在多线程的情况下, 数据在遍历的过程中遍历结果正确, 但是效率又足够高, 所以在iterator遍历的过程中增加了modCount(记录修改次数的同步机制), 由于设计的不够完美, 但是在单线程的情况下, 如果我们在遍历的过程中, 调用源集合的修改结构的方法修改了源集合类(modCount会变得不同步), 会导致并发修改异常产生.
//一般用的是for each循环
//注意:for each循环默认调用的也是iterator,所以也会出现并发修改异常的情况
//数组也能使用for each循环,但默认调用的是fori循环
// 1, List是Collection的子接口
// 2, List所描述的数据结构是线性表
// 3, List有序
// 4, List允许存储重复元素
// 5, List允许存储null
ListIterator类型
boolean hasNext() : 判断后面是否还有元素可以遍历
E next() : 向后遍历
void remove() : 删除刚刚遍历过的元素
boolean hasPrevious() : 判断前面是否还有元素可以遍历
E previous() : 向前遍历
int nextIndex() : 向后遍历的下标位置
int previousIndex() : 向前遍历的下标位置
void set(E e) : 修改刚刚遍历过的元素位置
void add(E e) : 在当前遍历位置添加一个元素
subList: 是一个视图方法
注意: java中的集合类中有很多视图方法, 要注意视图返回的对象实际上并没有真正持有那个数据, 它持有的是一个标记和引用, 它通过这个标记和引用指向源数据
注意: 视图方法也伴随着并发修改异常, 原因和iterator迭代一样, 都是在已经开始了使用, 但是还未使用完成之前, 调用了源集合类的修改结构方法, 让源集合类发生了改变, 会导致迭代或者视图有问题(不再准确), 所以会抛出并发修改异常, 所以我们在使用iterator的时候和使用subList这种视图方法的时候, 不要在使用过程中调用了源集合类的修改结构方法.
//ArrayList是List的一个子类,描述的是一个线性表
//ArrayList底层使用的是一个数组
//底层数组默认容量是10,扩容机制是1.5倍
//允许null,允许重复,有序,线程不安全
clone:返回的是一个浅表副本,复制该数组以及引用,但引用指向的内容还是一样的
//Vector是List的一个自实现,1.0的时候出现的
//Vector底层使用的是一个数组
//数组默认容量是10,扩容机制:若存在一个增量,则扩容的是原容量+增量,默认是扩容2倍
//有序,允许重复,允许null,线程安全
//Stack是Vector的一个子类,底层基本上是复用Vector中的参数、结构、方法
//如果使用栈结构的时候最好使用Deque接口,而非stack,在使用多态的时候注意要根据实际的需求调用方法
//LinkedList是List的一个子类,同时也是Deque的一个子实现
//LinkedList可以作为线性表、普通队列、双端队列、栈
//LinkedList底层使用的是一个双向链表
//LinkedList有序、允许重复、允许null、线程不安全
// LinkedList 的api的分类
// 1, 来自于Collection: 常见的, 添加和删除,
// 2, 来自于List: 根据下标的操作
// 3, 来自Queue: 队列方法, offer, poll, peek
// 4, 来自于Deque: 双端队列: offerFirst, offerLast, pollF...; addFirst, addLast..
// 栈: push, pop,
遵循要实现什么数据结构就使用什么方法的原则
//Queue接口是Collection接口的一个子接口,描述的数据结构是队列
//Queue有序
//Queue允许重复
//Queue不允许null(LinkedList除外,因为他主要作为List的子类)
//不能存储null的原因:poll方法会返回一个null来表示没有数据可以出队列了,如果存储null有可能会产生歧义
//Deque的Queue的一个子接口,可以是普通队列、双端队列、栈
//Deque有序,不允许null(除了LinkedList),允许重复
//ArrayDeque是Deque的一个具体子实现
//ArrayDeque描述的数据结构是 普通队列、双端队列、栈
//ArrayDeque底层使用的是一个循环数组
//底层数组容量是16,扩容机制是原来的2倍
//允许重复,不允许null,有序,线程不安全
//如果指定一个容量,那么得到的最终结果是一个大于该值的最小的2的幂值
// 阻塞队列: 是一个容量优先的队列
// 当这个队列存储数据满的时候, 添加线程等待
// 当这个队列没有存储任何数据的时候, 删除线程等待
在使用按时等待的方法的时候注意,该方法并不会一直等待,如果有数据可以操作了会立即执行,如果超过等待的时间了会自动结束
//Set接口是Collection接口的子类,描述的数据接口是集合
//所有子类都是不允许重复
//有些子类允许null,有些不允许null
//有些子类有序,有些无序
//HashSet是Set接口的一个具体子类实现
//HashSet底层持有一个HashMap,存储都使用HashMap,并且作为一个key存在
//不允许重复,允许null,无序,线程不安全
//HashSet的一个子类,底层持有LinkedHashMap
//存储到底层的LinkedHashMap中,并作为key存在
//有序(双向链表),不允许重复,允许null,线程不安全
//是Set的一个子类,底层是红黑树
//底层持有一个TreeMap,并且通过TreeMap存储Key值
//有序,不允许重复,不允许null
//存储的数据必须可以比较,自身实现了compare接口或者在构造方法中传入一个比较器
Map是Map集合系列的顶级接口
Map中存储的是key-value数据,键值对:具有自我描述性
Map中所有的子类都不允许重复
Map中有些子类允许null,有些不允许null
Map中有些子类有序,有些无序
void clear()
从此映射中移除所有映射关系(可选操作)。
boolean containsKey(Object key)
如果此映射包含指定键的映射关系,则返回 true。
boolean containsValue(Object value)
如果此映射将一个或多个键映射到指定值,则返回 true。
Set<Map.Entry<K,V>> entrySet()
返回此映射中包含的映射关系的 Set 视图。
boolean equals(Object o)
比较指定的对象与此映射是否相等。
V get(Object key)
返回指定键所映射的值;如果此映射不包含该键的映射关系,则返回 null。
int hashCode()
返回此映射的哈希码值。
boolean isEmpty()
如果此映射未包含键-值映射关系,则返回 true。
Set<K> keySet()
返回此映射中包含的键的 Set 视图。
V put(K key, V value)
将指定的值与此映射中的指定键关联(可选操作)。
void putAll(Map<? extends K,? extends V> m)
从指定映射中将所有映射关系复制到此映射中(可选操作)。
V remove(Object key)
如果存在一个键的映射关系,则将其从此映射中移除(可选操作)。
int size()
返回此映射中的键-值映射关系数。
Collection<V> values()
返回此映射中包含的值的 Collection 视图。
//HahshMap是Map的一个具体子实现。描述的数据结构是哈希表
//HashMap底层存储使用的是数组+链表+红黑树
//底层数组容量默认16,扩容机制是2倍,如果指定容量的话是得到一个大于等于该值的最小2的幂值
//不允许重复,有序,允许null,线程不安全
//加载因子默认0.75,实际能存储的阈值是数组长度*加载因子,注意这里存储指的是键值对数据的个数而非数组长度
//HashMap中链表的长度超过8到达9的时候有可能会转换为红黑树,如果数组长度小于64就会优先扩容,在扩容的过程中,原来下标为x位置的值得到的新下标有可能是x和x+原数组长度,就会减少链表的长度,可能就不会转换为红黑树
//在扩容和删除的过程中有可能会导致红黑树变为链表,在扩容的过程中可能会导致红黑树的拆分,当任意一个部分的节点数量小于6(提供一个缓冲的区间)就会转换为链表,同时如果根节点,根节点的左右节点,根节点的左节点的左节点中有一个为null,也会转变为链表
//hash值的计算:充分散列
//对于重复的定义:hash值相同,同时也要相equals或者==
//LinkedHashMap是HashMap的一个子类,基本上复用了HashMap的底层结构
//额外维护了一个双向链表,用来记录元素的顺序
//有序,允许null,不允许重复,线程不安全
//TreeMap是Map的子类,底层的结构是红黑树
//有序,不允许null,不允许重复(大小一样),线程不安全
//存入TreeMap的必须自身实现compare接口或者在构造方法中传入一个比较器
与HashMap的区别:
Hashtable是1.0出现的,线程安全
底层存储使用的是数组+链表
不允许null
数组容量默认是11,扩容机制是2倍+1