Java集合

0. Java 集合

也称容器,主要由两大接口派生而来:
Collection接口:主要用于存放单一元素,又有三个主要的子接口:List、Set、Queue
Map接口,主要用于存放键值对。

1. Collection

1.1 List

1.1.1 ArrayList:object[]数组,频繁访问和只在末尾增删时使用

        底层是数组队列,相当于动态数组(容量动态增长)。在添加大量元素前,可以使用ensureCapacity操作来增加ArrayList实例的容量。

重要方法:

添加:add(obj)

删除:remove(obj)、remove(index)、clear()

是否存在:contains(obj)

获取索引元素:get(index)

替换索引元素:set(int index, E element)

大小:size()

截取部分:subList(int fromIndex, int toIndex)

删除指定索引之间存在的元素:removeRange(int fromIndex, int toIndex)

排序:sort()

转数组:toArray()、转字符串:toString()

指定容量大小:ensureCapacity(int minCapacity)

返回指定元素最后一次出现的位置:lastIndexOf(Object obj)

继承于AbstractList,实现了 ListRandomAccessCloneablejava.io.Serializable 这些接口。
  • List : 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。
  • RandomAccess :这是一个标志接口,表明实现这个接口的 List 集合是支持 快速随机访问 的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
  • Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
                单纯拷贝引用地址的动作就是浅拷贝
                如果拷贝一个对象时不是简单的将地址引用拷贝出来,而是新建了一个对象,这种方式就是深拷贝,不会共享内存
  • Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。
   ArrayList 中可以存储任何类型的对象,包括 null 值。不过,不建议向ArrayList 中添加 null 值, null 值无意义,会让代码难以维护比如忘记做判空处理就会导致空指针异常。 

ArrayList与LinkedList区别:

  • 底层数据结构: ArrayList 底层使用的是 Object 数组LinkedList 底层使用的是 双向链表 数据结构
  • 插入和删除是否受元素位置的影响:即数组与链表的区别

    ArrayList 采用数组存储,默认在列表末尾添加,O(1)。但在指定位置 i 插入和删除元素为 O(n)。因为要移动元素。

    LinkedList 采用链表存储,头尾插入或者删除元素不受元素位置的影响,O(1),但在指定位置 i 插入和删除元素为 O(n) ,因为需要先移动到指定位置。

  • 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList(实现了 RandomAccess 接口) 支持。快速随机访问就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
  • 内存空间占用: ArrayList 的空间浪费主要体现在在 list 列表的结尾会预留一定的容量空间,而 LinkedList 的空间花费则体现在它的每一个元素都需要消耗比 ArrayList 更多的空间(因为要存放直接后继和直接前驱以及数据)。

ArrayList 和 Vector 的区别?

  • ArrayListList 的主要实现类,底层使用 Object[]存储,适用于频繁的查找工作,线程不安全
  • VectorList 的古老实现类,底层使用Object[] 存储,线程安全

ArrayList初始化的三种方法:

1. 默认容量,无参构造函数:实际上初始化赋值的是一个空数组。当真正对数组进行添加元素操作时,才真正分配容量。即向数组中添加第一个元素时,数组容量扩为默认值
 2. 用户指定容量,有参构造函数
3. 构造包含指定collection元素的列表

1.1.2 LinkedList:双向链表,需迭代访问和头尾增删时使用

需要用到 LinkedList 的场景几乎都可以使用 ArrayList 来代替,并且,性能通常会更好!

为什么不能实现RandomAccess接口:

底层数据结构是链表,内存地址不连续,只能通过指针来定位,不支持随机快速访问。

LinkedList 实现了以下接口:

  • List : 表明它是一个列表,支持添加、删除、查找等操作,并且可以通过下标进行访问。
  • Deque :继承自 Queue 接口,具有双端队列的特性,支持从两端插入和删除元素,方便实现栈和队列等数据结构。
  • Cloneable :表明它具有拷贝能力,可以进行深拷贝或浅拷贝操作。
  • Serializable : 表明它可以进行序列化操作,也就是可以将对象转换为字节流进行持久化存储或网络传输,非常方便。

插入元素:

add(E element):末尾插入
add(int index, E element) :指定位置

获取元素:

getFirst():第一个。

getLast():最后一个。

get(int index):指定位置。

删除元素:

  • removeFirst():删除并返回第一个。
  • removeLast():删除并返回最后一个。
  • remove(E e):删除链表中首次出现的指定元素,如果不存在则返回 false。
  • remove(int index):删除并返回指定位置。
  • void clear():移除所有。

遍历:

for (String fruit : list)

1.1.3 Vector:object[]数组

1.2 Set

1.2.1 HashSet:无序,唯一。

实现了HashMap和Set接口,底层采用HashMap保存元素。

允许有 null 值

不是线程安全的, 如果多个线程尝试同时修改 HashSet,则最终结果是不确定的。 您必须在多线程访问时显式同步对 HashSet 的并发访问。

1.2.2 LinkedHashSet:HashSet 的子类,内部通过 LinkedHashMap 实现

1.2.3 TreeSet:有序,唯一,红黑树(自平衡的排序二叉树)

三者异同:

同:都是 Set 接口的实现类,都能保证元素唯一,并且都不是线程安全的。
异:底层数据结构不同
主要区别在于底层数据结构不同。HashSet 的底层数据结构是哈希表(基于 HashMap 实现)。LinkedHashSet 的底层数据结构是链表和哈希表,元素的插入和取出顺序满足 FIFO。TreeSet 底层数据结构是红黑树,元素是有序的,排序的方式有自然排序和定制排序。
底层数据结构不同又导致这三者的应用场景不同。HashSet 用于不需要保证元素插入和取出顺序的场景,LinkedHashSet 用于保证元素的插入和取出顺序满足 FIFO 的场景,TreeSet 用于支持对元素自定义排序规则的场景。

无序性和不可重复性的含义是什么

  • 无序性不等于随机性 ,无序性是指存储的数据在底层数组中并非按照数组索引的顺序添加 ,而是根据数据的哈希值决定的。
  • 不可重复性是指添加的元素按照 equals() 判断时 ,返回 false,需要同时重写 equals() 方法和 hashCode() 方法。

Set和List的区别

  • Set 接口实例存储的是无序的,不重复的数据。List 接口实例存储的是有序的,可以重复的元素。

  • Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。

  • List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变。

1.3 Queue

1.3.1 PriorityQueue:

1.3.2 DelayQueue:延迟队列,用于实现延时任务,按照到期时间升序编排任务

1.3.3 ArrayDeque:可扩容动态双向数组

Queue与Deque的区别:

Queue 是单端队列,只能从一端插入元素,另一端删除元素,遵循 先进先出 规则。Queue 扩展了 Collection 的接口。

Deque 是双端队列,在队列的两端均可以插入或删除元素。Deque 扩展了 Queue 的接口。

2. Map 

2.1 HashMap:

是一个散列表,主要用来存放键值对,键和值类型可不同。
继承于AbstractMap,实现了 Map、Cloneable、java.io.Serializable 接口
是非线程安全的,不支持线程同步
HashMap 是无序的,即不会记录插入的顺序。
table数组+链表/红黑树 组成,当链表长度大于等于阈值(默认为 8)(将链表转换成红黑树前会判断,如果当前数组的长度小于 64,那么会选择先进行数组扩容,而不是转换为红黑树)时,将链表转化为红黑树,以减少搜索时间。

红黑树:避免二叉查找树退化成单链表(根和叶子是黑的,从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点)

重要参数:

  • DEFAULT_INITIAL_CAPACITY Table数组的初始化长度: 1 << 42^4=16(为什么要是 2的n次方?)
  • MAXIMUM_CAPACITY Table数组的最大长度: 1<<302^30=1073741824
  • DEFAULT_LOAD_FACTOR 负载因子:默认值为0.75。 当元素的总个数>当前数组的长度 * 负载因子。数组会进行扩容,扩容为原来的两倍(todo:为什么是两倍?)
  • TREEIFY_THRESHOLD 链表树化阙值: 默认值为 8 。表示在一个node(Table)节点下的值的个数大于8时候,会将链表转换成为红黑树
  • UNTREEIFY_THRESHOLD 红黑树链化阙值: 默认值为 6 。 表示在进行扩容期间,单个Node节点下的红黑树节点的个数小于6时候,会将红黑树转化成为链表。
  • MIN_TREEIFY_CAPACITY = 64 最小树化阈值,当Table所有元素超过该值,才会进行树化(为了防止前期阶段频繁扩容和树化过程冲突)。

重要方法:

  • 添加键值对:put(key, value) 、putAll(Map m) 添加所有,若重复会替换、putifAbsent(key, value) 不存在则添加
  • 获取 key 对应的 value:get(key)
  • 删除 key 对应的键值对(key-value):remove(key)
  • 删除所有键值对(key-value):clear()
  • 计算元素数量:size()
  • 迭代元素:for-each 
  • 检查是否存在指定的 key/value 对应的映射关系:containsKey(key)、containsValue(value)
  • 判空:isEmpty()
  • 替换指定的 key 对应的 value:replace(key, newValue)
  • 返回所有 key :keySet()
  • 返回所有value:values()
// 创建 HashMap 对象 Sites
HashMap<Integer, String> Sites = new HashMap<Integer, String>();

// 添加键值对
Sites.put(1, "Google");
Sites.put(2, "Runoob");

// 大小
System.out.println(Sites.size());  // 2

// 输出
System.out.println(Sites);  // {1=Google, 2=Runoob}
System.out.println(Sites.get(1));  // Google

// 迭代
// 输出 key 和 value
for (Integer i : Sites.keySet()) {
    System.out.println("key: " + i + " value: " + Sites.get(i));
}
// key: 1 value: Google
// key: 2 value: Runoob

// 输出每一个value:Google, Runoob,
for(String value: Sites.values()) {
    System.out.print(value + ", ");
}

// 删除
Sites.remove(1);
System.out.println(Sites);  // {2=Runoob}

Sites.clear();
System.out.println(Sites); // {}

2.2 LinkedHashMap

继承自HashMap,底层仍然是基于拉链式的散列结构,即由数组和链表或红黑树组成。
LinkedHashMap在上面结构的基础上,增加了一条双向链表,使得上面的结构可以保持键值对的插入顺序。同时通过对链表进行相应的操作,实现了访问顺序相关逻辑。

2.3 Hashtable:数组+链表,数组是Hashtable的主体,链表为了解决哈希冲突而存在。

2.4 TreeMap:红黑树(自平衡的排序二叉树)

参考:Java集合常见面试题总结(上) | JavaGuide

Java 集合框架 | 菜鸟教程

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值