ArrayUtils 源码阅读有感 :) (commons-lang3)

这两天刚好在等待分配的过程中想着创建自己的代码库的,但是后来想想世界如此之大,咱想到的东东各位大牛基本上都免费提供了,为哈不双手接上呢,鼓掌,感谢!

好了,先说个 ArrayUtils 的大概吧:
顾名思义,这货就是用来进行 array 操作的哦。不过这个工具类很大有6000行左右的说,提供的功能也就相对来说比较完备的。大概有以下几大类方法(其实一般都是 overloading):

  • EMPTY_… 数组
    这一类型的属性是不太会直接使用,一般会作为以下方法中的返回值
  • toString
    在操作数组的时候,往往我们会需要查看数组的具体值,尤其是在调试的时候,自己在进行相应的查看操作时通常会比较麻烦的。
    下面代码使用的是 JDK 自带的工具方法,也是不错的选择,不过呢在 ArrayUtils 对于 null 值是可以指定替代的值。
System.out.println(Arrays.deepToString(array))
  • hashcode
    生成一个数组的 hashCode,尤其是多维的数组哦
  • toMap
    这个应该算是创建 Map 的一个简便方案,主要是将二维数组转换为 Map,当然第二维需要有两位元素,分别作为 Map 的 key 和 value
  • toArray
    当我们有不定数量的同类型元素时便可以使用此方法,它为我们将提供的元素放入到数组中,然后返回,很大方有么有
  • clone
    对于数组的复制来说,使用这个方法最好不过了,首先 ArrayUtils 对其做了充分的 overloading,其次它的实现方式是调用数组类型本身的方法 clone(),要知道这个底层的实现是 native 方式的,那个速度可想而知了。
    当然其实比较明显的是既然 ArrayUtils 自己都调用的是数组的内置方法为啥我们还要多此一举呢?确实这里边的差距不大,但是,如果需要克隆的 array 是个 null 呢?所以呢,ArrayUtils 还是为咱们省了点事儿的,不是说优秀的程序员都爱偷懒儿么:)
  • nullToEmpty
    想起第一个属性没?它就起到作用啦,只要 array 是 null 或者 array.length() == 0 那么你就会得到这个 Empty 数组,只是它是不可更改的单元素数组罢了,可以作为一个标志来使用
  • subArray
    同样,这个方法确实我们自己实现起来也不难,不过它做了大量的判断,最后通过调用 native 方法来提升元素复制的效率
  • isSameLength
    同样,精简比较过程,能处理 array 为 null 的情形
  • getLength
    通过 native 方式获取长度
  • reverse
    overloading 了许多的反转数组的方法,而且也支持反转数组中的部分子元素
  • indexOf
    这个方法的实现其实是有超出我的猜测的。在没有看之前自己想想是不是用了什么神奇的方法呢?用的还是比较朴素的做法,逐个比对。不过呢,在其的 overloading 中有对一次性查询多个相同数据的优化方案,这个会结合最后的方法进行解释的。
  • lastIndexOf
    和上一个类似,只是顺序调了个头。
  • contains
    这个的实现方式也有点出乎我的意料,原来是调用 indexOf 来判断的。
  • toPrimitive
    将数组元素统统转换为基本类型,当数组中存在 null 时一定要指定替换的值,否则不包含替代值的方法的调用会报错的,毕竟 null 与任意基本类型都没有对应值,所以一定注意。
  • toObject
    与上面方法的功能相反
  • isEmpty
    简化了的数组判断方法
  • addAll
    绝对的将两个数组进行合并哦,null数组也能搞定,只要给它,它就能帮你把它们给结合咯
  • copyArrayGrow1
    有没有觉得这个方法的名字高大上,非常清楚以至于我差点不相信自己的眼睛了。。说实话我重来没有在方法名称上使用过阿拉伯数字的说。顾名思义,先 copy 再增大 1 个空间。当然真正实现中是先创建一个 length + 1 的新数组的,然后么在用 native 方法进行 copy。
  • add
    额,忘记说了,上边那个方法是 private 的,人家是为此方法服务的,有木有很方便,数组的第一印象应该就是长度的不可变性吧:) 可是人家 ArrayUtils 就不吃你这套,帮咱们实现了数组的加法。神奇吧,不过调用的方法就是上边的 copyArrayGrow1,是不是很坦率。
  • remove
    如同 add, remove 的就过也能让数组长度少 1,这与 add 刚好是反过来的哈
  • removeElement
    这个与上一个方法的区别在于,上一个是根据元素的 index 来删的,而下一个则是要先找到指定的数组元素时才进行删除
  • removeAll
    这个方法还是蛮有意思的,下边会有贴出源代码供共赏
  • removeElements
    此方法与上边类似,只不过参数已经是相应的 数组下标了

重点介绍 removeAll,它也是一个 overloading 型选手:

static Object removeAll(final Object array, final int... indices) {
        final int length = getLength(array);
        int diff = 0; // 需要移掉的元素数量

        if (isNotEmpty(indices)) {
            Arrays.sort(indices);//排序对于进行移除有关键性的作用,从小到大

            int i = indices.length;//总共移除的数量
            int prevIndex = length;//从数组最后开始计算
            while (--i >= 0) {//这里所做移除元素合法性的验证,以及去除掉超出数组范围的下标,重复的下标
                final int index = indices[i];
                if (index < 0 || index >= length) {
                    throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length);
                }
                if (index >= prevIndex) {
                    continue;
                }
                diff++;//只要符合情况就增加 1
                prevIndex = index;
            }
        }
        final Object result = Array.newInstance(array.getClass().getComponentType(), length - diff);//表示最终形成的新数组的长度
        if (diff < length) {//如果要移除的数量等于数组本身的长度,肯定是个空数组了
            int end = length; // 当前原数组的剩余长度
            int dest = length - diff; // 当前未填充目标数组长度
            for (int i = indices.length - 1; i >= 0; i--) {
                final int index = indices[i];//指向第 i 个需要移除的元素
                if (end - index > 1) { // 等价于 (cp > 0),end 只是长度
                    final int cp = end - index - 1;
                    dest -= cp;//目标数组接收元素的起始下标
                    System.arraycopy(array, index + 1, result, dest, cp);
                    // Afer this copy, we still have room for dest items.
                }
                end = index;
            }
            if (end > 0) {//只要移除的下标不是0时,就需要执行此 block
                System.arraycopy(array, 0, result, 0, end);
            }
        }
        return result;
    }

另外一种 removeAll 与上一个方法是类似的,就不贴代码了,而调用它的方法(overloading)中使用了一个比较有意思的算法,贴出来共赏:

 public static boolean[] removeElements(final boolean[] array, final boolean... values) {
        if (isEmpty(array) || isEmpty(values)) {
            return clone(array);//clone原数组
        }
        final HashMap<Boolean, MutableInt> occurrences = new HashMap<Boolean, MutableInt>(2); // boolean 型也就只有两种了,true , false :)
        for (final boolean v : values) { // 这里就是这个算法的预处理了,很重要的哦
            final Boolean boxed = Boolean.valueOf(v);
            final MutableInt count = occurrences.get(boxed);
            if (count == null) {
                occurrences.put(boxed, new MutableInt(1));
            } else {
                count.increment(); // 最终统计出 false, true 分别移除的数量
            }
        }
        final BitSet toRemove = new BitSet();
        //这里做一件比较有意思的事
        //我们可以出这样一道题目,给出一个数组和一组需要在数组中进行查询的数集合,让找出各自所对应的下标,而且允许有重复对象存在
        //最普通的做法是穷举需要查找的数,与数组中元素一一比较,这个基本上就是o(NM)的复杂度了
        //但是这个算法就高端点了,能力有限,还不清楚这个复杂度要咋写-_-||,大神了解的还望指点指点,感激不尽啊:)
        //不过其实本质和普通方法一样,但是呢减少了对于重复元素的穷举所消耗的时间

        for (final Map.Entry<Boolean, MutableInt> e : occurrences.entrySet()) {
            final Boolean v = e.getKey();
            int found = 0;
            // 根据重复的个数进行相应次数的查找
            for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) {
                found = indexOf(array, v.booleanValue(), found);
                if (found < 0) {
                    break;
                }
                toRemove.set(found++);
            }
        }
        return (boolean[]) removeAll(array, toRemove);
    }

有错误之处还请指出,谢拉 :)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值