读源码:commons-lang2.4的ArrayUtils类

鲁淇
2023-12-01

apache的commons包写的还是很好的,熟练运用可以事半功倍。计划写系列文章读一下lang包的主要几个类,主要是说明作用,有时候会说一下自己读源码的感悟。

代码内容

常量

ArrayUtils 类刚开始定义了很多空数组静态变量:Object空数组,Class空数组,String空数组,八大基本类型空数组,及八大基本类型的包装类的空类型,然后定义了一个INDEX_NOT_FOUND=-1表示index不存在的情形。根据《代码整洁之道》,意义明确、一看就懂很重要!!!所以有时候定义常量是更好的选择,而不是写个注释”-1代表找不到”。

toString()

toString()方法用到了ToStringBuilder类,这个类可以简单灵活的控制toString方法的输出,下面是ToStringBuilder的简单用法介绍:
public class Person {
   String name;
   int age;
   boolean smoker;

   ...

   public String toString() {
       return new ToStringBuilder(this).
       append("name", name).
       append("age", age).
       append("smoker", smoker).
       toString();
   }
 }  
 //调用输出示例:Person@7f54[name=Stephen,age=29,smoker=false]

hashCode()

方法用到了HashCodeBuilder类,同上面差不多

isEquals()

方法用到了EqualsBuilder类,同上面差不多

Map toMap(Object[] array)

方法将array转换为map,要求array必须是Entry一维数组或者类似这样的new String[][] { {"RED", "#FF0000"},  {"GREEN", "#00FF00"},  {"BLUE", "#0000FF"}}

clone(Object[]/基本类型[] array)

方法可以处理null,然后则直接调用 array.clone()方法了
此处值得注意的是:array的clone方法是浅拷贝方法。浅拷贝是仅仅拷贝对象本身,深拷贝则拷贝对象及对象的引用。一个数组如果是基本数据类型,不管浅拷贝还是深拷贝,结果都是将数据都复制一遍,然后赋值;而如果一个数组的元素是object,浅拷贝则只拷贝每个object的引用,深拷贝得现将每个object复制一份,然后将每个新的object引用组成一个数组才赋值回去。

subarray(Object[]/基本类型[] array, int startIndexInclusive, int endIndexExclusive)

获取数组的一部分子数组,获取的数组包括startIndex,不包含endIndex;如果数组长度小于0等于0,则返回一个空数组;如果array是null,则返回null

isSameLength(Object[]/基本类型[] array1, Object[]/基本类型[] array2)

比较两个数组是否是同一长度。将null的长度视为0比较的。

getLength(Object array)

获取数组长度,null返回0

isSameType(Object array1, Object array2)

比较两个数组是否是同样的类型。如果有一个是null,直接抛出异常,怎么感觉这块不好好处理null了。。。。

reverse(Object[]/基本类型[] array)

反转数组。运用了将第一个和最后一个交换,第二个和倒数第二个交换,第三个和倒数第三个交换....的方法

int indexOf(Object[]/基本类型[] array, Object/基本类型 objectToFind, int startIndex)

查找某个对象在数组中的位置,找不到返回INDEX_NOT_FOUND即-1。当时基本类型时,判断相等用==就可以,但是Object的时候,要注意用equals()方法,同时代码将null也单独处理,更显得严谨。 
多态:当startIndex不写时,默认从0开始。 lastIndexOf、contains(判断indexOf返回值是否为-1)方法思想类似,不再赘述。

toPrimitive(包装类型[] array, 基本类型 valueForNull)

primitives有原始、原始人的意思,此处是基本类型。所以本方法是将包装类型转为基本类型的方法,valueForNull是如果包装类型为null的填充代替内容。 多态:不写valueForNull则用默认的方法。

toObject(基本类型[] array)

将基本类型转换为包装类型的方法,不再赘述

isEmpty()

判断数组是否为空,估计很常用。下面代码说明为了语义清晰的写法的表达方式,当然,其实更深层可以是为了更符合代码设计原则。
 public static boolean isEmpty(Object[] array) {
     if (array == null || array.length == 0) {
         return true;
     }
     return false;
 }
//代码内容是很简单,但下面分析下这两种写法,第一种写法语义清晰,见名知意,建议多采用:
if (ArrayUtils.isEmpty(array)){ ... }
if (array == null || array.length == 0){ ... }
//再举个判断邮箱的例子:
if (Pattern.compile("^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$").match(email)){
    ...
}
//这种方法写的人一头雾水,改的话也倒是还好,但不熟悉的人就会看半天,用下面这种写会更好一点
if(checkEmail(email)){
    ...
}
private boolean checkEmail(String email){
    String check = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";    
    Pattern regex = Pattern.compile(check);    
    Matcher matcher = regex.matcher("dffdfdf@qq.com");    
    boolean isMatched = matcher.matches();    
    return isMatched;
}

Object[]/基本类型[] addAll(Object[]/基本类型[] array1, Object[]/基本类型[] array2)

  返回array1追加array2的数组。

Object[]/基本类型[] add(Object[]/基本类型[] array1, Object/基本类型 element)

将element添加至最后一个位置,都先调用私有的copyArrayGrow1方法将数组长度+1,然后将element添加上去。评价:效率有点低,能不这么做最好,能提前处理好数组长度最好

Object[]/基本类型[] add(Object[]/基本类型[] array1, int index, Object/基本类型 element)

在某个位置上添加一个元素,统一调用私有的Object add(Object array, int index, Object element, Class clss)方法,这种多态方式极力推荐。

remove(Object[]/基本类型[] array, int index) 和 removeElement(Object[]/基本类型[] array, Object/基本类型 element)

remove移除某位置上的元素,removeElement仅仅移除匹配的第一个元素。他们共同调用私有的 Object remove(Object array, int index)方法,同样的推荐写法.

思考

数组和Object的关系?

一言以蔽之:数组就是一个Object,里面包含了一组基本类型的数据或者一组对象的引用。

为什么ArrayList里面的方法都是Object数组和基本数据类型数组多态分开的?

首先,基本数据类型的父类并不是Object。分开写思路清晰,可以防止基本数据类型的自动装箱机制对返回结果的影响。
当然,部分方法用了Object array来表示数组,是因为数组本身是一个object,直接调用其它方法就没必要多写那么多了。比如下面这个方法:
public static int getLength(Object array) {
    if (array == null) {
        return 0;
    }
    return Array.getLength(array);
}
 类似资料: