最近读《Java核心技术卷一》集合章节后,颇有感想,发现了很多之前忽略的小细节,整理一下,书写下来。当然如果你和我一样之前使用集合仅限于基本的添加删除遍历等基础操作,可以通过本文章了解一些方便的集合用法。当然更建议去读读《Java核心技术》这本书。

映射(Map)更新值

我们有一个需求,每次操作将Map中的一个键对应的值进行累加,这不难做到因为put方法会覆盖之前的值:

map.put("java", map.get("java") + 1);

这里就忽略了一个问题,java这个键可能还没有进行第一次添加,get将得到一个null和1进行运算就会报错。当然你可以写一个判断来处理,但那样太不优雅了不是吗?
Map中有两个方法可以简易的解决这个问题:getOrDefault和putIfAbsent
前者会在获取的键为空时获取一个默认值:

// 此代码即可解决问题,若key为空就获取默认值0
map.put("java", map.getOrDefault("java", 0) + 1);

后者为若映射中没有这个键或者键引用null时才会进行添加:

map.putIfAbsent("java", 0); // 键引用null或者没有键时才会设置
map.put("java", map.get("java") + 1);

如果我嫌弃上两种方案不够优雅,Map中还有一个方法可以做的更好。merge方法可以简化这个操作:

map.merge("java", 1, Integer::sum);

这里用到了方法引用,可以将其看成一个特殊的Lambda表达式。

这里如果key不存在就会将key设置为1,若存在就会通过key的值来调用方法引用,这里给的是Integer::sum,也就是将key的值和1进行加法运算再返回。

根据条件删除

场景很简单,给定一个条件,将集合中满足这个条件的元素删除。
我们可以自行遍历集合,当然遍历方式很多,可以用foreach,或者迭代器等等。当然自己遍历就显得很笨拙。

Collection接口中提供了removeIf方法可以通过传入的Lambda表达式来传入条件确定是否删除元素。
例如下代码就将列表中等于a的字符串删除:

list.removeIf((e) -> e.equals("a"));

当然Map接口中时没有这个方法的,不能直接使用。但是我们可以通过映射获取它的视图,视图是一个实现了Collection的某个子类的集合肯定存在removeIf方法。
例如下列带码就将map中key为a的元素删除:

Set<Map.Entry<String, String>> entries = map.entrySet();
entries.removeIf((e) -> e.getKey().equals("a"));

如果条件为确定一个列表的范围,或者排序集/排序映射的大小范围。可以使用建立子范围视图来进行删除。
例如将列表中10~19个元素删除:

List<String> list = objects.subList(10, 20);
list.clear();

subList用于生成从n(包含)到m(不包含)下标的子范围视图。

再比如我们要从一个TreeSet中删除比15值大的元素:

SortedSet<Integer> set = integers.tailSet(14);
set.clear();

tailSet用于生成大于或等于n的所有元素的子范围视图。

当然子范围的用途不仅限于删除,这里只是展示了它可以用来做条件删除。

Q.E.D.


深至缄默,如云漂泊