1、概述
引言:Java集合工具类分为两种,一种是 java 自带的(无需下jar包,jdk自带),一种是其他公司提供的(如Apache)。在公司开发的过程中较为常用的是Apache提供的工具类,学习了解这些工具类是十分有必要的。
2、Collections工具类
Collections 是一个操作 Set、List 和 Map 等集合的工具类 ,位于 java.util 包中。Collections 中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作, 还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
2.1、排序操作(均为static方法)
Collections 类提供了多种排序方法,可以对集合进行反转、洗牌、自然排序和自定义排序等操作。
- reverse(List list):反转集合中元素的顺序。
- shuffle(List list):随机打乱集合中元素的顺序。
- sort(List list):对集合进行自然升序排序。
- sort(List list, Comparator c):根据自定义比较器对集合进行排序。
- swap(List list, int i, int j):交换集合中指定位置的两个元素。
示例代码
List<String> list = new ArrayList<>();
list.add("AAA");
list.add("BBB");
list.add("CCC");
list.add("DDD");
list.add("EEE");
System.out.println("原始顺序:" + list);
// 反转
Collections.reverse(list);
System.out.println("反转后:" + list);
// 洗牌
Collections.shuffle(list);
System.out.println("洗牌后:" + list);
// 自然升序
Collections.sort(list);
System.out.println("自然升序后:" + list);
// 交换
Collections.swap(list, 2, 4);
System.out.println("交换后:" + list);
输出结果:
原始顺序:[AAA, BBB, CCC, DDD, EEE]
反转后:[EEE, DDD, CCC, BBB, AAA]
洗牌后:[DDD, AAA, EEE, BBB, CCC]
自然升序后:[BBB, AAA, DDD, EEE, CCC]
交换后:[BBB, AAA, CCC, EEE, DDD]
2.2、查找、替换
Collections 类提供了多种查找方法,可以用于查找集合中的最大值、最小值、元素出现的次数等。
- binarySearch(List list, Object key):二分查找法,前提是集合已经排序。
- max(Collection coll):返回集合中的最大元素。
- max(Collection coll, Comparator comp):根据自定义比较器返回集合中的最大元素。
- min(Collection coll):返回集合中的最小元素。
- min(Collection coll, Comparator comp):根据自定义比较器返回集合中的最小元素。
- fill(List list, Object obj):使用指定对象填充集合。
- frequency(Collection c, Object o):返回指定对象在集合中出现的次数。
- void copy(List dest,List src):将src中的内容复制到dest中,注意src的长度必须小于dest的长度,否则报错
- boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值
示例
System.out.println("最大元素:" + Collections.max(list));
System.out.println("最小元素:" + Collections.min(list));
System.out.println("出现的次数:" + Collections.frequency(list, "AAA"));
// 没有排序直接调用二分查找,结果是不确定的
System.out.println("排序前的二分查找结果:" + Collections.binarySearch(list, "AAA"));
Collections.sort(list);
// 排序后,查找结果和预期一致
System.out.println("排序后的二分查找结果:" + Collections.binarySearch(list, "AAA"));
Collections.fill(list, "GGG");
System.out.println("填充后的结果:" + list);
结果
最大元素:CCC
最小元素:BBB
出现的次数:1
排序前的二分查找结果:0
排序后的二分查找结果:1
填充后的结果:[GGG, GGG, GGG, GGG, GGG]
2.3、集合线程不安全--同步控制【废弃】
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题 ,可以查看JUC。
//返回的list即为线程安全的list
List list = new ArrayList();
List list = Collections.synchronizedList(list)
注意:
SynchronizedList 的源码实现非常简单,只是在方法内部使用 synchronized 关键字加了一层锁。
static class SynchronizedList<E>
extends SynchronizedCollection<E>
implements List<E> {
private static final long serialVersionUID = -7754090372962971524L;
final List<E> list;
SynchronizedList(List<E> list) {
super(list); // 调用父类 SynchronizedCollection 的构造方法,传入 list
this.list = list; // 初始化成员变量 list
}
// 获取指定索引处的元素
public E get(int index) {
synchronized (mutex) {return list.get(index);} // 加锁,调用 list 的 get 方法获取元素
}
// 在指定索引处插入指定元素
public void add(int index, E element) {
synchronized (mutex) {list.add(index, element);} // 加锁,调用 list 的 add 方法插入元素
}
// 移除指定索引处的元素
public E remove(int index) {
synchronized (mutex) {return list.remove(index);} // 加锁,调用 list 的 remove 方法移除元素
}
}
那这样的话,其实效率和那些直接在方法上加 synchronized 关键字的 Vector、Hashtable 差不多(JDK 1.0 时期就有了),而这些集合类基本上已经废弃了,几乎不怎么用。
public class Vector<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
// 获取指定索引处的元素
public synchronized E get(int index) {
if (index >= elementCount) // 如果索引超出了列表的大小,则抛出数组下标越界异常
throw new ArrayIndexOutOfBoundsException(index);
return elementData(index); // 返回指定索引处的元素
}
// 移除指定索引处的元素
public synchronized E remove(int index) {
modCount++; // 修改计数器,标识列表已被修改
if (index >= elementCount) // 如果索引超出了列表的大小,则抛出数组下标越界异常
throw new ArrayIndexOutOfBoundsException(index);
E oldValue = elementData(index); // 获取指定索引处的元素
int numMoved = elementCount - index - 1; // 计算需要移动的元素个数
if (numMoved > 0) // 如果需要移动元素
System.arraycopy(elementData, index+1, elementData, index,
numMoved); // 将数组中的元素向左移动一位
elementData[--elementCount] = null; // 将最后一个元素设置为 null,等待垃圾回收
return oldValue; // 返回被移除的元素
}
}
正确的做法是使用并发包下的 CopyOnWriteArrayList、ConcurrentHashMap。
2.4、不可变集合
Collections 类提供了多种方法来创建不可变集合,这些集合一旦创建,就无法进行修改操作。
emptyXxx():创建一个空的不可变集合。singletonXxx():创建一个只包含一个元素的不可变集合。unmodifiableXxx():为指定集合创建一个不可变视图。
示例代码:
List emptyList = Collections.emptyList();
emptyList.add("非空"); // 抛出 UnsupportedOperationException
System.out.println(emptyList);
输出结果:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.AbstractList.add(AbstractList.java:148)
at java.util.AbstractList.add(AbstractList.java:108)
at com.itwanger.s64.Demo.main(Demo.java:61)
2.5、singletonList()方法
首先,singletonList() 方法返回的列表只有一个元素。与Arrays.asList() 方法不同,singletonList() 方法返回一个不可变的列表。若集合只有一个元素,建议使用该方法。
换句话说,不允许在singletonList() 方法返回的列表上进行结构性和非结构性更改。测试可以快速说明这一点:
List<String> singletonList = Collections.singletonList("ONE");
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(
() -> singletonList.add("TWO")
);
assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(
() -> singletonList.set(0, "A brand new string")
);
如果我们运行测试,它就会通过。因此,无论我们是向列表中添加元素还是更改列表中的元素,它都会抛出UnsupportedOperationException。
值得一提的是,如果我们看一下返回的列表类的源代码,与其他List实现不同,返回列表中的单个元素不存储在数组或任何其他复杂的数据结构中。相反,列表直接保存元素对象:
private static class SingletonList<E> extends AbstractList<E> implements RandomAccess, Serializable {
...
private final E element;
SingletonList(E obj) {element = obj;}
...
}
因此,它将占用更少的内存。
2.6、unmodifiableMap()方法
相关方法:
unmodifiableMap()方法的功能:返回一个不可变的Map类型视图unmodifiableSortedMap()方法的语法:public static SortedMap unmodifiableMap(Map m);
注意事项:
- unmodifiableMap()方法在
java.util包中可用 - unmodifiableMap()方法用于获取给定Set(集合)的不可修改视图
- unmodifiableMap()方法是一个静态方法,可以使用类名进行访问,如果尝试使用类对象访问该方法,那么也不会出现任何错误。
- 返回给定集合的不可修改视图时,unmodifiableMap()方法不会引发异常
示例
import java.util.Collections;
import java.util.Map;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
Map<Integer, String> tm = new TreeMap<Integer, String>();
// 通过使用put()方法是添加
// 树状图中的对象
tm.put(11, "java");
tm.put(88, "CSharp");
tm.put(99, "Python");
tm.put(120, "C");
tm.put(130, "ErLang");
// 显示树图
System.out.println("TreeMap: " + tm);
// 通过使用unmodifiableSortedMap()方法是
Map um = Collections.unmodifiableMap(tm);
um.put(880, "GO");//此处会产生异常
}
}
2.7、其他操作
Collections 类还提供了一些其他常用的方法:
addAll(Collection<? super T> c, T... elements):将多个元素添加到集合中。disjoint(Collection<?> c1, Collection<?> c2):判断两个集合是否没有交集。
示例代码:
List<String> allList = new ArrayList<>();
Collections.addAll(allList, "TTT", "KKK", "AAA");
System.out.println("addAll 后:" + allList);
System.out.println("是否没有交集:" + (Collections.disjoint(list, allList) ? "是" : "否"));
输出结果:
addAll 后:[TTT, KKK, AAA]
是否没有交集:否
3、Spring 和 Apache 的集合工具类
3.1、CollectionUtils工具类
除了 JDK 提供的 Collections 工具类,Spring 和 Apache 也提供了各自的集合工具类,如 org.springframework.util.CollectionUtils 和 org.apache.commons.collections.CollectionUtils
Apache 的 CollectionUtils 提供了更多实用的方法,如集合判空、集合操作(交集、并集、差集等)。
- 引入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.4</version>
</dependency>
3.1.1、对两个集合进行交、并、补、差集操作
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
List<Integer> list2 = new ArrayList<>();
list2.add(2);
list2.add(4);
// 获取并集
Collection<Integer> unionList = CollectionUtils.union(list, list2);
System.out.println(unionList);
// 获取交集
Collection<Integer> intersectionList = CollectionUtils.intersection(list, list2);
System.out.println(intersectionList);
// 获取交集的补集
Collection<Integer> disjunctionList = CollectionUtils.disjunction(list, list2);
System.out.println(disjunctionList);
// 获取差集
Collection<Integer> subtractList = CollectionUtils.subtract(list, list2);
System.out.println(subtractList);
输出结果:
[1, 2, 3, 4]
[2]
[1, 3, 4]
[1, 3]
3.1.2、判空操作
通过CollectionUtils工具类的isEmpty方法可以轻松判断集合是否为空,isNotEmpty方法判断集合不为空。
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
if (CollectionUtils.isEmpty(list)) {
System.out.println("集合为空");
}
if (CollectionUtils.isNotEmpty(list)) {
System.out.println("集合不为空");
}
3.1.3、从一个对象的集合中,获取某一属性的集合
从一个对象的集合中,获取某一属性的集合。如,从人员信息集合中获取人员姓名的集合
List<Map<String, Object>> list = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "张三");
map1.put("age", 1);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "李四");
map2.put("age", 2);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "王五");
map3.put("age", 3);
list.add(map1);
list.add(map2);
list.add(map3);
List<String> nameList = (List<String>) CollectionUtils.collect(
list, new Transformer() {
public Object transform(Object arg0) {
Map<String, Object> map = (Map<String, Object>) arg0;
return map.get("name");
}
});
System.out.println(nameList);
运行结果
[张三, 李四, 王五]
3.1.4、从一个对象集合中过滤出只符合要求的对象
List<Map<String, Object>> list = new ArrayList<>();
List<Map<String, Object>> tarList = new ArrayList<>();
Map<String, Object> map1 = new HashMap<>();
map1.put("name", "张三");
map1.put("age", 1);
Map<String, Object> map2 = new HashMap<>();
map2.put("name", "张三");
map2.put("age", 2);
Map<String, Object> map3 = new HashMap<>();
map3.put("name", "王五");
map3.put("age", 3);
list.add(map1);
list.add(map2);
list.add(map3);
CollectionUtils.filter(list, new Predicate(){
public boolean evaluate(Object arg0) {
Map<String, Object> map = (Map<String, Object>)arg0;
return "张三".equals((String)map.get("name"))
&& 2 == Integer.parseInt(String.valueOf(map.get("age")));
}
});
System.out.println(list);
运行结果
[{name=张三, age=2}]
3.2、MapUtils工具类
导入依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.2</version>
</dependency>
3.2.1、获取基础类型的值
可以从指定map中获取常用基础类型的值,都会判断map,map为null返回null,get结果为null返回null:
示例
Map<String, Object> nullMap = null;
Map<String, Object> map = new HashMap<>();
map.put("user", new User());
map.put("boolean", true);
Assert.assertTrue(MapUtils.getBoolean(map, "boolean"));
//转换失败,返回默认值false
Assert.assertFalse(MapUtils.getBoolean(map, "user",false));
//目标map为null,返回null
Assert.assertNull(MapUtils.getBoolean(nullMap, "boolean"));
//目标map为null,返回默认值false
Assert.assertFalse(MapUtils.getBoolean(nullMap, "boolean", false));
Assert.assertTrue(MapUtils.getBooleanValue(map, "boolean"));
//转换失败,返回默认值false
Assert.assertFalse(MapUtils.getBooleanValue(map, "user",false));
//目标map为null,返回false
Assert.assertFalse(MapUtils.getBooleanValue(nullMap, "boolean"));
//目标map为null,返回默认值false
Assert.assertFalse(MapUtils.getBooleanValue(nullMap, "boolean", false));
map.put("integer", 5);
Assert.assertEquals(Integer.valueOf(5), MapUtils.getInteger(map, "integer"));
//转换失败,返回默认值5
Assert.assertEquals(Integer.valueOf(5), MapUtils.getInteger(map, "integer",5));
//目标map为null,返回null
Assert.assertEquals(null, MapUtils.getInteger(nullMap, "integer"));
//目标map为null,返回默认值5
Assert.assertEquals(Integer.valueOf(5), MapUtils.getInteger(nullMap, "integer", 5));
Assert.assertEquals(5, MapUtils.getIntValue(map, "integer"));
//转换失败,返回默认值5
Assert.assertEquals(5, MapUtils.getIntValue(map, "user",5));
//目标map为null,返回0
Assert.assertEquals(0, MapUtils.getIntValue(nullMap, "integer"));
//目标map为null,返回默认值5
Assert.assertEquals(5, MapUtils.getIntValue(nullMap, "integer", 5));
3.2.2、Map判空
HashMap<String, Object> map = new HashMap<>();
map.put("name","zhangsan");
map.put("sex",true);
map.put("age",34);
map.put("money",null);
// 使用MapUtils.isEmpty方法和MapUtils.isNotEmpty方法对Map进行空判断。
boolean empty = MapUtils.isEmpty(map);
boolean notEmpty = MapUtils.isNotEmpty(map);
3.2.3、数组操作
HashMap<String, Object> map = new HashMap<>();
map.put("name","zhangsan");
map.put("sex",true);
map.put("age",34);
map.put("money",null);
将二维数组放入Map中,使用MapUtils.putAll方法。
String[][] user = {{"names","zhangfsan"},{"sexs","1f"}};
Map map1 = MapUtils.putAll(map, user);