Java集合知识汇总


Java集合知识汇总

一、List(列表)——线程不安全的数据结构

1.1、List数组(ArrayList)

底层:一个可动态扩容的数组,与普通数组的区别就是它是没有固定大小的限制

特点:顺序存储,读速度、更新快,增删慢;内存相邻,根据Index读取的时间复杂度是O(1);可以存储重复元素,但线程不安全

扩容机制:如果当前元素放不下,则扩容至1.5倍,且扩容空间大于等于1

1.2、List链表(LinkedList)

底层:节点,存下数据的引用对象(非数据),节点之间使用引用相关联

特点:顺序存储,实现了Queue、Deque接口,可作为队列使用;查找慢,增删快,可以存储重复元素,但线程不安全

扩容机制:无固定大小,不需要扩容

Api

public E getLast() //获取最后一个元素
public E removeFirst() // 移除第一个元素,并返回
public E removeLast() // 移除最后一个元素,并返回
public void addFirst(E e) //加入头部
public void addLast(E e) //加入尾部
public void add(E e) //加入尾部
public boolean contains(Object o) //是否包含 元素 o
public E peek() //获取头部第一个元素
public E element() // 获取头部第一个元素,不存在则报错
public E poll() //获取头部第一个元素,并移除
public boolean offer(E e) // 调用 add(E e)
public boolean offerFirst(E e) // 调用 addFirst
public boolean offerLast(E e) // 调用 addLast
public void push(E e) //在头部压入一个元素
public E pop() //弹出第一个元素,并移除。不存在则报错

1.3、List总结

ArrayList和LinkedList:

共同点:顺序存储,可以存储重复元素,线程不安全

不同点:ArrayList底层数组,内存相邻,因此擅长读改,LinkedList底层节点之间的使用引用相关联,因此擅长增删

Iterator 和 fast-fail、fail-safe机制

  • Java Iterator(迭代器)不是一个集合,它是一种用于访问集合的方法,可用于迭代 List 和 Set 等集合,主要有hashNext(),next(),remove()三种方法

  • fail-fast 是Java集合(Collection)的一种错误机制。当多个线程对同一个集合进行修改结构操作,使用集合的迭代器iterator,会首先检测是否有对集合的并发修改,进而产生ConcurrentModificationException 异常提示

  • fail-safe:保证在对任何集合结构的修改操作都基于先复制再修改 进行的,即先copy一个新的集合对象,然后对新的集合对象进行修改,最后将新的集合对象替换掉老的集合对象(老的集合对象的地址指向新的集合对象)。java.util.concurrent包下采用的是fail-safe机制

    • 缺点1:对集合的复制copy会产生大量的对象,造成内存空间的浪费
    • 缺点2:无法保证集合迭代过程中获取的集合数据是最新的内容

如何保证线程安全呢?

  • plan_A:使用 Vector
  • plan_B:使用 Collections.synchronized() 返回线程安全的 List
  • plan_C:使用 CopyOnWriteArrayList

Collections.synchronized()举例:

List<String> list = Collections.synchronizedList(new ArrayList<String>());
list.add("1");
synchronized (list) {
    Iterator i = list.iterator(); // Must be in synchronized block
    while (i.hasNext()) {
        System.out.println(i.next());
    }
}
Collections.synchronizedList()添加数据不用锁(底层代码已经实现),遍历数据用锁

CopyOnWriteArrayList 的线程安全

CopyOnWriteArrayList 在写的时候会加锁,为了保证写安全,会在写操作时复制一个新数组来操作,然后覆盖旧的数组,不会影响读的性能

CopyOnWriteArrayList 的缺点

  • CopyOnWrite 在进行写操作的时候,内存里会同时驻扎两个对象的内存,导致内存的浪费
  • CopyOnWrite 容器只能保证数据的最终一致性,不能保证数据的实时一致性。如果你希望写入的的数据,马上能读到,请不要使用CopyOnWrite容器,没有阻塞等待的概念

CopyOnWriteArrayList 和 Collections.synchronizedList 区别

  • CopyOnWriteArrayList 的写操作性能较差,而多线程的读操作性能较好

  • Collections.synchronizedList的写操作性能比CopyOnWriteArrayList在多线程操作的情况下要好很多,而读操作因为是采用了 synchronized关键字的方式,其读操作性能并不如CopyOnWriteArrayList

List的Api

boolean contains(Object o) // 是否包含 o
boolean isEmpty(); // 是否为空
int size(); //集合元素
Iterator<E> iterator(); // 返回迭代器
Object[] toArray(); // 转为 Object数组
<T> T[] toArray(T[] a); // 转为具体类型数组
boolean add(E e); // 加入尾部
boolean remove(Object o); // 移除 o
boolean containsAll(Collection<?> c); //是否报考 集合 c
boolean addAll(Collection<? extends E> c);// 合并 c 
boolean retainAll(Collection<?> c);//保留只存在集合 c 的元素
void clear(); // 清除集合元素
void sort(Comparator<? super E> c) //根据 Comparator 排序
E get(int index); // 根据下标获取 元素
E set(int index, E element); // 设置第 index 的元素
E remove(int index); // 移除 第 index 的元素
<E> List<E> of(E e1.....) // jdk 9
List<E> copyOf(Collection<? extends E> coll)  // 复制

二、Vector(向量)——ArrayList 线程安全的翻版

底层:数组

特点:查询快,线程安全,线程安全

Api

boolean synchronized contains(Object o);
boolean synchronized isEmpty();
boolean synchronized containsAll(Collection<?> c);
boolean addAll(Collection<? extends E> c);
public synchronized boolean add(E e)
public synchronized E get(int index);
public synchronized E set(int index, E element);
public synchronized E firstElement()
public synchronized void removeElementAt(int index)
public synchronized E lastElement()
public synchronized void setElementAt(E obj, int index)
public synchronized E remove(int index)
public void clear()
Iterator<E> iterator();

三、 Stack(栈)——后进先出的线性表

底层:数组

特点:用于模拟"栈"这种数据结构(LIFO后进先出),线程安全,允许 null 值

Api

public E push(E item) //推入栈顶
public synchronized E pop() // 弹出栈顶元素,不存在则报错
public synchronized E peek() // 获取栈顶元素,不移除
public boolean empty()  // 栈是否为空
public synchronized int search(Object o) // 搜索元素 

image-20211110190612681

四、Map——不属于Collection的映射关系

底层:key到value的映射

特点:可以使任何引用类型的数据,但key不能重复,无法遍历( 没有继承 Collection 接口)

4.1、TreeMap(1.8JDK)

底层:红黑树

特点:不允许出现重复的key,可以插入null键,null值,可以对元素进行排序,无序集合(插入和遍历顺序不一致)。

img

4.2、HashMap

底层:数组+链表+红黑树,空参的HashMap初始容量是16,默认加载因子为0.75。

特点:具有很快的访问速度,最多允许一条记录的键为 null,线程不安全,存储无序。

HashMap如何处理hash冲突

  • 链地址法:如果存在 hash 碰撞,则创建一链表存储相同的元素
  • 开放定址法:就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到,并将记录存入。
  • 再哈希:同时构造多个不同的哈希函数,当哈希地址Hi=RH1(key)发生冲突时,再计算Hi=RH2(key)……,直到冲突不再产生。
  • 建立公共溢出区:将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

4.3、HashTable(基本被淘汰)

特点:HashTable的操作几乎和HashMap一致,主要的区别在于HashTable为了实现多线程安全,在几乎所有的方法上都加上了synchronized锁,而加锁的结果就是HashTable操作的效率十分低下。

HashMap和HashTable区别

  • HashMap允许有一个键为null,允许多个值为null,但HashTable不允许键或值为null。
  • Hash映射:HashMap的hash算法通过非常规设计,将底层table长度设计为2的幂,使用位与运算代替取模运算,减少运算消耗;而HashTable的hash算法首先使得hash值小于整型数最大值,再通过取模进行散射运算

4.4、LinkedHashMap

底层:HashMap和双向链表合二为一

特点:LinkedHashMap的元素存取过程基本与HashMap基本类似,只是在细节实现上稍有不同。当然,这是由LinkedHashMap本身的特性所决定的,因为它额外维护了一个双向链表用于保持迭代顺序。此外,LinkedHashMap可以很好的支持LRU算法。

4.5、WeakHashMap

底层:散列表,它存储的内容也是键值对(key-value)映射

特点:键和值都可以是 null,WeakHashMap的键是“弱键”。在 WeakHashMap 中,当某个 key 不再被强引用使用时,会被从WeakHashMap中被 JVM 自动移除,然后它对应的键值对也会被从WeakHashMap中移除。

4.6、ConcurrentHashMap(1.8JDK)

底层:底层数据结构是 数组 + 链表/红黑树

特点:ConcurrentHashMap 是 HashMap 的多线程安全版本。它使用了细粒度锁 和 cas 提高了在多线程环境的安全性和高并发

悲观锁与乐观锁

synchronized是悲观锁,这种线程一旦得到锁,其他需要锁的线程就挂起的情况就是悲观锁。

CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。

4.7、ConcurrentSkipListMap

底层:基于跳跃链表的实现的 map

特点:使用了 cas 技术实现线程安全性,高并发

ConcurrentSkipListMap 相比 ConcurrentHashMap 的优点

  • ConcurrentSkipListMap 的key是有序的

  • ConcurrentSkipListMap 支持更高的并发。ConcurrentSkipListMap的存取时间是log(N),和线程数几乎无关。也就是说在数据量一定的情况下,并发的线程越多,ConcurrentSkipListMap 越能体现出它的优势

4.8、NavigableMap 和 ConcurrentNavigableMap 操作 key 值的范围区间

原理

  • TreeMap 实现了 NavigableMap 。ConcurrentNavigableMap 高并发线程安全版的 TreeMap
  • NavigableMap 提供了针对给定搜索目标返回最接近匹配项的导航方法。

Api

K lowerKey(K key)  // 找到第一个比指定的key小的值
K floorKey(K key)  // 找到第一个比指定的key小于或等于的key
K ceilingKey(K key) // 找到第一个大于或等于指定key的值
K higherKey(K key) // 找到第一个大于指定key的值
Map.Entry<K,V> firstEntry() // 获取最小值
Map.Entry<K,V> lastEntry() // 获取最大值
Map.Entry<K,V> pollFirstEntry() // 删除最小的元素
Map.Entry<K,V> pollLastEntry() // 删除最大的元素
NavigableMap<K,V> descendingMap() //返回一个倒序的Map 
// 返回值小于 toKey 的 NavigableMap
NavigableMap<K,V> headMap(K toKey, boolean inclusive)
// 返回值大于 fromKey 的 NavigableMap
NavigableMap<K,V> tailMap(K fromKey, boolean inclusive)
// 返回值小于 toKey 大于 的 fromKey NavigableMap
NavigableMap<K,V> subMap(K fromKey, boolean fromInclusive, K toKey,   boolean toInclusive)

五、Set(集合)——元素唯一的无下标数组

特点:存储顺序无关,元素不可重复,如果加入重复元素,会保留最先加入的对象。存取速度快。但与List不同的是,Set还提供了equals(Object o)和hashCode(),供其子类重写,以实现对集合中插入重复元素的处理。

Api

方法 内容
boolean add(E o) 如果 set 中尚未存在指定的元素,则添加此元素(可选操作)
boolean addAll(Collection<? extends E> c) 如果 set 中没有指定 collection 中的所有元素,则将其添加到此 set 中(可选操作)
void clear() 移除 set 中的所有元素(可选操作)
boolean contains(Object o) 如果 set 包含指定的元素,则返回 true
boolean containsAll(Collection<?> c) 如果此 set 包含指定 collection 的所有元素,则返回 true
boolean equals(Object o) 比较指定对象与此 set 的相等性
int hashCode() 返回 set 的哈希代码值
boolean isEmpty() 如果 set 不包含元素,则返回 true
Iterator iterator() 返回在此 set 中的元素上进行迭代的迭代器
boolean remove(Object o) 如果 set 中存在指定的元素,则将其移除(可选操作)
boolean removeAll(Collection<?> c) 移除 set 中那些包含在指定 collection 中的元素(可选操作)。
boolean retainAll(Collection<?> c) 仅保留 set 中那些包含在指定 collection 中的元素(可选操作)
int size() 返回 set 中的元素数(其容量)
Object[] toArray() 返回一个包含 set 中所有元素的数组
T[] toArray(T[] a) 返回一个包含 set 中所有元素的数组;返回数组的运行时类型是指定数组的类型

5.1、TreeSet

底层:红黑树

特点:具有排序功能(自然排序(123456)和自定义排序,默认是自然排序),允许插入Null值,不允许插入重复元素,线程不安全。

image-20211111111501603

红黑树: 一种自平衡二叉查找树

img

5.2、HashSet

底层:哈希表(HashMap)

特点:不允许出现重复因素,允许插入Null值,元素无序(添加顺序和遍历顺序不一致),线程不安全。

基本操作:插入的元素被当做是HashMap的key,根据hashCode值来确定集合中的位置,由于Set集合中并没有角标的概念,所以并没有像List一样提供get()方法。当获取HashSet中某个元素时,只能通过遍历集合的方式进行equals()比较来实现。

5.3、LinkedHashSet

底层:链表和哈希表共同实现

特点:链表保证了元素的顺序与存储顺序一致,哈希表保证了元素的唯一性,效率高,但线程不安全。

5.4、ConcurrentSkipListSet

底层:基于 ConcurrentSkipListMap 实现

5.5、CopyOnWriteArraySet

底层:基于 CopyOnWriteArrayList 实现

5.6、BitSet

底层:long数组

特点:BitSet是位操作的对象,值只有 0 或 1 即false和true,内部维护了一个long数组,初始只有一个long,所以BitSet最小的size是64,当随着存储的元素越来越多,BitSet内部会动态扩充,最终内部是由N个long来存储。

Api

void and(BitSet set) // 两个BitSet 做与操作,结果并存入当前 BitSet
void andNot(BitSet set) //  两个BitSet 与非操作
void flip(int index) // 反转某一个指定 index 
boolean intersects(BitSet bitSet) // 是否有交集
int cardinality() //返回 true/1 的个数
void clear() // 重置
void clear(int startIndex, int endIndex) // startIndex~endIndex 重置
int nextSetBit(int startIndex) //检索在startIndex之后出现为1的第一位的索引
int nextClearBit(int startIndex) //检索在startIndex之后出现为0的第一位的索引

5.7、TreeSet和HashSet对比

共同点:允许插入Null值,不允许插入重复元素,线程不安全,可以被克隆,可以被序列化。

不同点:TreeSet对插入的元素进行排序,是一个有序的集合,HashSet无序。TreeSet底层使用红黑树,HashSet底层使用HashMap。

六、Queue(队列)——一端进一端出的线性表

底层:基于数组,链表实现

特点:存储有序

Api

boolean add(E e); //加入队列尾部
boolean offer(E e); // 加入队列尾部,并返回结果
E remove(); //移除头部元素
E poll();  // 获取头部元素,并移除
E element(); // 获取头部元素,不存在则报错
E peek(); // 获取头部元素,不移除

image-20211111164316977

6.1、PriorityQueue

底层:PriorityQueue是按优先级排序的队列,也就是说 vip 可以插队。优先队列要求使用 Java Comparable 和 Comparator 接口给对象排序,并且在排序时会按照优先级处理其中的元素。

6.2、PriorityBlockingQueue

底层:线程安全的PriorityQueue。

6.3、BlockingQueue

底层:BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。常用于线程的任务队列

6.4、DelayQueue

DelayQueue是一个没有边界BlockingQueue实现,加入元素必须实现Delayed接口。当生产者线程调用put之类的方法加入元素时,会触发 Delayed 接口中的compareTo方法进行排序。消费者线程查看队列头部的元素,注意是查看不是取出。然后调用元素的getDelay方法,如果此方法返回的值小0或者等于0,则消费者线程会从队列中取出此元素,并进行处理。如果getDelay方法返回的值大于0,则消费者线程阻塞到第一元素过期。

七、Deque(双向队列)——两端均可进出的线性表

特点:Deque接口代表一个"双端队列",双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可以当成队列使用、也可以当成栈使用

**Deque 的子类 **:LinkedList,ArrayDeque,LinkedBlockingDeque

Api

void addFirst(E e); //加入头部
void addLast(E e);  //加入尾部
boolean offerFirst(E e); //加入头部,并返回结果
boolean offerLast(E e); //加入尾部,并返回结果
E removeFirst(); // 移除第一个元素
E removeLast(); // 移除最后一个元素
E getFirst(); //获取第一个元素,不存在则报错
E getLast();  //获取最后一个元素,不存在则报错
E pollFirst(); //获取第一个元素,并移除
E pollLast(); //获取最后一个元素,并移除
E peekFirst(); //获取第一个元素
E peekLast(); // 获取最后一个元素
void push(E e); //加入头部
E pop(); //弹出头部元素

八、总结

增删改查的通用方法

操作方法 抛出异常 阻塞线程 返回特殊值 超时退出
插入元素 add(e) put(e) offer(e) offer(e, timeout, unit)
移除元素 remove() take() poll() pull(timeout, unit)
检查 element() peek()

20200318130238448


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM