集合
集合类的特点
a. 只能存储引用数据类型:因为泛型只能传入引用的数据类型
b. 可以自动地调整自己的大小
数组和集合类都是容器,它们有何不同?
a. 数组可以存储基本数据类型的数据,集合不可以。
b. 数组的长度是固定的,集合可以自动调整自己的大小。
c. 数组的效率高,相对来说集合效率比较低。
d. 数组没有API,集合有丰富的API。
集合类特点的回答顺序:
1, 是谁的子类, 描述了什么数据结构
2, 底层结构: 数组, 链表, 数组+链表, 数组(默认的初始容量, 扩容机制)
3, 有序, 重复, null 问题
4, 线程安全与否
Collection
特点
// 1, Collecion是Collection集合体系的顶级接口 // 2, Collection有些子实现有序, 有些子实现无序 // 3, Collection有些子实现允许存储重复元素, 有些子实现不允许存储重复元素 // 4, Collection有些子实现允许存储null, 有些子实现不允许存储null
API
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循环
List
// 1, List是Collection的子接口 // 2, List所描述的数据结构是线性表 // 3, List有序 // 4, List允许存储重复元素 // 5, List允许存储null
API
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
ArrayList的特点
//ArrayList是List的一个子类,描述的是一个线性表 //ArrayList底层使用的是一个数组 //底层数组默认容量是10,扩容机制是1.5倍 //允许null,允许重复,有序,线程不安全
clone:返回的是一个浅表副本,复制该数组以及引用,但引用指向的内容还是一样的
Vector
特点
//Vector是List的一个自实现,1.0的时候出现的 //Vector底层使用的是一个数组 //数组默认容量是10,扩容机制:若存在一个增量,则扩容的是原容量+增量,默认是扩容2倍 //有序,允许重复,允许null,线程安全
Stack
//Stack是Vector的一个子类,底层基本上是复用Vector中的参数、结构、方法 //如果使用栈结构的时候最好使用Deque接口,而非stack,在使用多态的时候注意要根据实际的需求调用方法
LinkedList
特点
//LinkedList是List的一个子类,同时也是Deque的一个子实现 //LinkedList可以作为线性表、普通队列、双端队列、栈 //LinkedList底层使用的是一个双向链表 //LinkedList有序、允许重复、允许null、线程不安全
API
// LinkedList 的api的分类 // 1, 来自于Collection: 常见的, 添加和删除, // 2, 来自于List: 根据下标的操作 // 3, 来自Queue: 队列方法, offer, poll, peek // 4, 来自于Deque: 双端队列: offerFirst, offerLast, pollF...; addFirst, addLast.. // 栈: push, pop,
遵循要实现什么数据结构就使用什么方法的原则
Queue
Queue接口的特点
//Queue接口是Collection接口的一个子接口,描述的数据结构是队列 //Queue有序 //Queue允许重复 //Queue不允许null(LinkedList除外,因为他主要作为List的子类) //不能存储null的原因:poll方法会返回一个null来表示没有数据可以出队列了,如果存储null有可能会产生歧义
Deque
//Deque的Queue的一个子接口,可以是普通队列、双端队列、栈 //Deque有序,不允许null(除了LinkedList),允许重复
ArrayDeque
//ArrayDeque是Deque的一个具体子实现 //ArrayDeque描述的数据结构是 普通队列、双端队列、栈 //ArrayDeque底层使用的是一个循环数组 //底层数组容量是16,扩容机制是原来的2倍 //允许重复,不允许null,有序,线程不安全 //如果指定一个容量,那么得到的最终结果是一个大于该值的最小的2的幂值
BlockingQueue
// 阻塞队列: 是一个容量优先的队列 // 当这个队列存储数据满的时候, 添加线程等待 // 当这个队列没有存储任何数据的时候, 删除线程等待 在使用按时等待的方法的时候注意,该方法并不会一直等待,如果有数据可以操作了会立即执行,如果超过等待的时间了会自动结束
Set
Set接口的特点
//Set接口是Collection接口的子类,描述的数据接口是集合 //所有子类都是不允许重复 //有些子类允许null,有些不允许null //有些子类有序,有些无序
HashSet
//HashSet是Set接口的一个具体子类实现 //HashSet底层持有一个HashMap,存储都使用HashMap,并且作为一个key存在 //不允许重复,允许null,无序,线程不安全
LinkedHashSet
//HashSet的一个子类,底层持有LinkedHashMap //存储到底层的LinkedHashMap中,并作为key存在 //有序(双向链表),不允许重复,允许null,线程不安全
TreeSet
//是Set的一个子类,底层是红黑树 //底层持有一个TreeMap,并且通过TreeMap存储Key值 //有序,不允许重复,不允许null //存储的数据必须可以比较,自身实现了compare接口或者在构造方法中传入一个比较器
Map
Map是Map集合系列的顶级接口
Map中存储的是key-value数据,键值对:具有自我描述性
Map中所有的子类都不允许重复
Map中有些子类允许null,有些不允许null
Map中有些子类有序,有些无序
API
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 视图。
HashMap
//HahshMap是Map的一个具体子实现。描述的数据结构是哈希表 //HashMap底层存储使用的是数组+链表+红黑树 //底层数组容量默认16,扩容机制是2倍,如果指定容量的话是得到一个大于等于该值的最小2的幂值 //不允许重复,有序,允许null,线程不安全 //加载因子默认0.75,实际能存储的阈值是数组长度*加载因子,注意这里存储指的是键值对数据的个数而非数组长度 //HashMap中链表的长度超过8到达9的时候有可能会转换为红黑树,如果数组长度小于64就会优先扩容,在扩容的过程中,原来下标为x位置的值得到的新下标有可能是x和x+原数组长度,就会减少链表的长度,可能就不会转换为红黑树 //在扩容和删除的过程中有可能会导致红黑树变为链表,在扩容的过程中可能会导致红黑树的拆分,当任意一个部分的节点数量小于6(提供一个缓冲的区间)就会转换为链表,同时如果根节点,根节点的左右节点,根节点的左节点的左节点中有一个为null,也会转变为链表 //hash值的计算:充分散列 //对于重复的定义:hash值相同,同时也要相equals或者==
LinkedHashMap
//LinkedHashMap是HashMap的一个子类,基本上复用了HashMap的底层结构 //额外维护了一个双向链表,用来记录元素的顺序 //有序,允许null,不允许重复,线程不安全
TreeMap
//TreeMap是Map的子类,底层的结构是红黑树 //有序,不允许null,不允许重复(大小一样),线程不安全 //存入TreeMap的必须自身实现compare接口或者在构造方法中传入一个比较器
Hashtable
与HashMap的区别:
Hashtable是1.0出现的,线程安全
底层存储使用的是数组+链表
不允许null
数组容量默认是11,扩容机制是2倍+1