【语言热身】Java 集合的使用
本文题目难度标识:🟩简单,🟨中等,🟥困难。
集合 Collection
/Map
集合增删改查速览:
![]() |
Collection |
List |
Queue |
Deque |
Set |
---|---|---|---|---|---|
增 | add(E e) |
add(int index,E element) |
抛异常:add(e) 返回特殊值: offer(e) |
抛异常:addFirst(e) 、addLast(e) 返回特殊值: offerFirst(e) 、offerLast(e) |
|
删 | remove(Object o) 移除指定对象clear() 清空所有内容 |
抛异常:remove() 返回特殊值: poll() |
抛异常:removeFirst() 、removeLast() 返回特殊值: pollFirst() 、pollLast() |
boolean remove(obj) 移除存在的元素返回 true ,移除不存在的元素返回 false |
|
改 | set(int index, E element) 请避免对空列表调用该函数 |
||||
查 | get(int index) |
抛异常:element() 返回特殊值: peek() |
抛异常:getFirst() 、getLast() 返回特殊值: peekFirst() 、peekLast() |
||
判断 | contains(Object o) isEmpty() |
栈结构的使用:
- 队列也可用作 LIFO(后进先出)堆栈。应优先使用此接口而不是遗留
Stack
类。 List
也可用于简单的栈。ls.add(e)
方法作为入栈,ls.remove(ls.size() - 1)
作为出栈。
变长数组可以使用 ArrayList 进行实现。
![]() |
Map |
说明 |
---|---|---|
增 | put(key,value) |
|
删 | remove(key) 删除 key 对应的键值对clear() 清空 map |
|
改 | put(key,newValue) |
|
查 | get(key) getOrDefault(key,defaultValue) |
|
判断 | containsKey(key) containsValue(value) |
|
集合 | entrySet() keySet() |
注意,没有关于值的集合。 |
getOrDefault
的使用有时候我们会编写这样的代码:map.getOrDefault(key,func());
,我们会误以为只有 map
不存在 key
时才会调用 func()
。事实上,Java 在传递参数给方法前,都会计算参数的值。因此即使 key 存在,func()
还是会执行一次。
如果我们需要 map
不存在时才执行 func()
,可以使用 computeIfAbsent
函数:map.computeIfAbsent(key,e->func());
或者使用三元运算符:int result = map.containsKey(key)? map.get(key) : func();
Map
的遍历
1 | // Map 遍历元组对 |
用 Map
计数
1 | Map<Character,Integer> map = new HashMap<>(); |
方法:hashmap.merge(key, value, remappingFunction)
merge()
方法会先判断指定的key
是否存在,如果不存在,则添加键值对到hashMap
中。merge()
返回值:如果key
对应的value
不存在,则返回该value
值,如果存在,则返回通过remappingFunction
重新计算后的值。- 重新映射函数可以使用 Lambda 表达式,比如:
(oldValue, newValue) -> oldValue + newValue)
1 | Map<Character,Integer> map = new HashMap<>(); |
哈希算法
有时候我们需要在集合中存储一个元素的二维坐标,我们不需要自己定义二元组存储,我们可以:
- 只存储第几个格子即可:
i*n+j
- 将各种参数组合成独一无二的
String
来作为 Key:"12,23,24"
或a=1,b=2,c=3
。如需分离,可使用string.split(regex)
函数进行分离。
我们还可以自己实现简单字符串的哈希算法。
不同哈希算法的粗略分析
下面列举出的算法是在通过例题 🟨 3137. K 周期字符串需要的最少操作次数 - 力扣(LeetCode) 前提下,对计算时间和复杂性权衡后的结果。
Hash 算法 | 时间 (ms) | 内存 (MB) | 时间击败比率 (%) |
---|---|---|---|
简易位运算 Hash | 23 | 45.2 | 100 |
简易乘法 Hash | 28 | 88.37 | |
Java Map hash() |
43 | 44.85 | 11.63 |
1 | // 乘法 Hash |
相关题目:
Array
与 List
之间的转换
两者之间的转换除了 for 循环外,还有以下方式。
Array2List:
1 | String[] array = {"A","B","C"}; // 基本类型数组的话必须是包装类型数组 |
【强制】使用工具类 Arrays.asList()
把数组转换成集合时, 不能使用其修改集合相关的方法, 它的 add
/ remove
/ clear
方法会抛出 UnsupportedOperationException
异常。
说明: asList
的返回对象是一个 Arrays
内部类, 并没有实现集合的修改方法。 Arrays.asList
体现的是适配器模式, 只是转换接口, 后台的数据仍是数组。
List2Array:
1 | List<String> ls = new ArrayList<String>(){{add("aa");add("bb");add("cc");}}; |
注意 String[] array = (String [])ls.toArray();
是不对的。因为 ls.toArray()
返回的 Object[]
类不能向下转型为 String[]
。有关 List
的 toArray
方法详看:站内文章这篇文章。
Set
转数组也可以使用toArray(T[] array)
。
【强制】 使用集合转数组的方法, 必须使用集合的 toArray(T[] array)
, 传入的是类型完全一致、 长度为 0 的空数组。
List
的复制
1 | // 底层源码涉及到了 Arrays.toArray() |
集合的排序
集合的排序详见:站内文章【语言热身】Java 中的排序
TreeSet
TreeMap
的一些常用方法
以下函数在与「线段树」有关的题目的题解下见得多。可以稍微了解下,毕竟玩 API 玩得好能节省时间。
tset.floor(E e)
方法返回在这个集合中小于或者等于给定元素的最大元素,如果不存在这样的元素,返回null
tset.ceiling(E e)
方法返回在这个集合中大于或者等于给定元素的最小元素,如果不存在这样的元素,返回null
tset.last()
tset.lower()
tmap.floorEntry(E e)
tmap.higherEntry(E e)
tmap.firstEntry()
tmap.subMap(E fromKey, E toKey)
:返回一个视图,其键范围从fromKey
(包括)到toKey
(不包括)。注意,子Map
中的更改会反映在原始Map
中。
相关题目: 🟨 729. 我的日程安排表 I - 力扣(LeetCode)
优先队列 PriorityQueue
优先队列使用了堆数据结构,堆的形式为数组。基本方法为 peek
、offer
、poll
。
因为 PriorityQueue
中插入删除的数据是泛型类, 可以是基本类型对应的包装类, 也可以是自定义类, 在向上调整和向下调整的过程中,需要不断地比较,如果数据没有指定比较规则,或者为 null
时,程序就会抛出异常。
1 | Queue<Integer> priorityQueue1 = new PriorityQueue<>(); // 默认数组容量 11 |
Java 集合中的 PriorityQueue
底层的堆默认是小根堆,如果要设置大根堆,就必须传入比较器, 并重写 compare
方法。