1. 前言
本文介绍的 ArrayUtils 以org.apache.commons.collections4.ArrayUtils 为例
源代码:org.apache.commons.collections4.ArrayUtils
自定义特征可以在自己服务内实现
在Java应用开发中,数组操作是我们日常编码的基础任务之一。你是否曾写过下面这样的代码?
1public boolean findUser(String[] users, String targetUser) { 2 if (users == null) { 3 return false; 4 } 5 for (int i = 0; i < users.length; i++) { 6 if (targetUser == null) { 7 if (users[i] == null) { 8 return true; 9 } 10 } else { 11 if (targetUser.equals(users[i])) { 12 return true; 13 } 14 } 15 } 16 return false; 17} 18 19public int findUserIndex(String[] users, String targetUser) { 20 if (users == null) { 21 return -1; 22 } 23 for (int i = 0; i < users.length; i++) { 24 // ... 类似的重复代码 25 } 26 return -1; 27} 28
这种手动遍历数组的方式不仅繁琐、重复,更重要的是需要处理各种边界情况(如空数组、null元素、起始位置等),容易出错且代码可读性差。一个精心设计的工具类可以封装这些通用逻辑,让数组操作变得简单而安全。本文将深入解析ArrayUtils工具类的实现原理,展示如何通过简洁的API解决数组操作的常见痛点。
2. 常用方法
2.1 包含检查方法
2.1.1 方法签名
1static boolean contains(Object[] array, Object objectToFind) 2
2.1.2 应用示范
1public class ArrayUtilsDemo { 2 public static void main(String[] args) { 3 String[] fruits = {"apple", "banana", "orange", null}; 4 5 // 检查元素是否存在 6 System.out.println(ArrayUtils.contains(fruits, "apple")); // true 7 System.out.println(ArrayUtils.contains(fruits, "grape")); // false 8 System.out.println(ArrayUtils.contains(fruits, null)); // true 9 10 // 处理空数组场景 11 String[] emptyArray = null; 12 System.out.println(ArrayUtils.contains(emptyArray, "test")); // false 13 } 14} 15
2.1.3 关键源码
1static boolean contains(Object[] array, Object objectToFind) { 2 return indexOf(array, objectToFind) != -1; 3} 4
2.2 索引查找方法
2.2.1 方法签名
1static <T> int indexOf(T[] array, Object objectToFind) 2static int indexOf(Object[] array, Object objectToFind, int startIndex) 3
2.2.2 应用示范
1public class IndexOfDemo { 2 public static void main(String[] args) { 3 Integer[] numbers = {1, 2, 3, 2, 5, null, 2}; 4 5 // 查找元素首次出现位置 6 System.out.println(ArrayUtils.indexOf(numbers, 2)); // 1 7 System.out.println(ArrayUtils.indexOf(numbers, 10)); // -1 8 System.out.println(ArrayUtils.indexOf(numbers, null)); // 5 9 10 // 从指定位置开始查找 11 System.out.println(ArrayUtils.indexOf(numbers, 2, 2)); // 3 12 System.out.println(ArrayUtils.indexOf(numbers, 2, 4)); // 6 13 14 // 处理边界情况 15 System.out.println(ArrayUtils.indexOf(null, 1)); // -1 16 } 17} 18
2.2.3 关键源码
本质比较的是equals方法,如有需要可以重写equals方法
1static int indexOf(Object[] array, Object objectToFind, int startIndex) { 2 if (array == null) { 3 return -1; 4 } else { 5 if (startIndex < 0) { 6 startIndex = 0; 7 } 8 9 if (objectToFind == null) { 10 for(int i = startIndex; i < array.length; ++i) { 11 if (array[i] == null) { 12 return i; 13 } 14 } 15 } else { 16 for(int i = startIndex; i < array.length; ++i) { 17 if (objectToFind.equals(array[i])) { 18 return i; 19 } 20 } 21 } 22 23 return -1; 24 } 25} 26
3. 与Stream API的对比
3.1 相同功能的不同实现
ArrayUtils方式:
1// 检查包含 2boolean contains = ArrayUtils.contains(array, target); 3 4// 查找索引 5int index = ArrayUtils.indexOf(array, target); 6
Stream API方式:
1// 检查包含 2boolean contains = Arrays.stream(array) 3 .anyMatch(element -> Objects.equals(element, target)); 4 5// 查找索引(较为繁琐) 6int index = IntStream.range(0, array.length) 7 .filter(i -> Objects.equals(array[i], target)) 8 .findFirst() 9 .orElse(-1); 10
3.2 性能对比
1public class PerformanceComparison { 2 public static void main(String[] args) { 3 String[] largeArray = createLargeArray(100000); 4 5 // ArrayUtils性能测试 6 long start1 = System.nanoTime(); 7 boolean result1 = ArrayUtils.contains(largeArray, "target"); 8 long time1 = System.nanoTime() - start1; 9 10 // Stream API性能测试 11 long start2 = System.nanoTime(); 12 boolean result2 = Arrays.stream(largeArray) 13 .anyMatch("target"::equals); 14 long time2 = System.nanoTime() - start2; 15 16 System.out.println("ArrayUtils耗时: " + time1 + "ns"); 17 System.out.println("Stream API耗时: " + time2 + "ns"); 18 } 19} 20
3.3 适用场景分析
| 特性 | ArrayUtils | Stream API |
|---|---|---|
| 代码简洁性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 可读性 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 函数式支持 | ⭐⭐ | ⭐⭐⭐⭐⭐ |
| 链式操作 | 不支持 | 支持 |
| 空安全 | ⭐⭐⭐⭐⭐ | 需要额外处理 |
4. 总结
ArrayUtils通过简洁的API设计,为我们提供了强大的数组操作能力。它的主要优势包括:
- 代码简洁性:将常见的数组操作封装成简单的方法调用
- 空安全:妥善处理null值场景,提高代码健壮性
- 灵活性:支持从指定位置开始搜索,满足不同业务需求
- 类型安全:使用泛型确保类型一致性
虽然这个工具类功能相对基础,但它体现了良好的设计思想:专注于解决特定问题,保持接口简单,处理边界情况。在实际开发中,我们可以借鉴这种设计思路,创建更多实用的工具类来提升开发效率。
建议使用场景:适合中小型数组的快速操作,对于性能要求极高的场景建议考虑其他优化方案。
《ArrayUtils:Java数组操作的瑞士军刀》 是转载文章,点击查看原文。