这两天刚好在等待分配的过程中想着创建自己的代码库的,但是后来想想世界如此之大,咱想到的东东各位大牛基本上都免费提供了,为哈不双手接上呢,鼓掌,感谢!
好了,先说个 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);
}
有错误之处还请指出,谢拉 :)