apache-common-lang3系列之一

本系列的apache-common-lang3的代码版本是3.9,本系列博客主要的作用是工具书的作用。能够快速知道自己想要的功能是否已经实现,是否需要自己重新写代码。
v3.9版本常用类手册

ArrayUtils

clone方法

这里的clone方法是一个重载函数,所有的实现逻辑是一样的,都是调用Oject对象的clone方法。

源码

 public static <T> T[] clone(final T[] array) {
    if (array == null) {
        return null;
    }
    return array.clone();
}

insert方法

insert 实质创建一个新数组,并把旧数组元素和要插入的元素copy到新数组相应的位置。然后返回新数组。

这里需要注意的是

  1. ArrayUtils.insert(index, array, null)= cloned copy of 'array'
    这种调用情况的clone是浅克隆,任何对新旧数组相关的元素的修改都会影响另一方(基本数据类型、String类型除外)
  2. 正常调用:采用了System.arraycopy(values, 0, result, index, values.length),这个和浅克隆的情况一样。任何对新旧数组相关的元素的修改都会影响另一方(基本数据类型、String类型除外)

源码

public static <T> T[] insert(final int index, final T[] array, final T... values) {

        if (array == null) {
            return null;
        }
        if (values == null || values.length == 0) {
            //这个地方的clone是调用的Object的clone方法
            //所以是浅克隆
            return clone(array);
        }
        //是否越界
        if (index < 0 || index > array.length) {
            throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + array.length);
        }
        //获取数组中的元素的classType
        final Class<?> type = array.getClass().getComponentType();
         // OK, because array and values are of type T
        @SuppressWarnings("unchecked")
        final
        T[] result = (T[]) Array.newInstance(type, array.length + values.length);

        System.arraycopy(values, 0, result, index, values.length);
        if (index > 0) {
            System.arraycopy(array, 0, result, 0, index);
        }
        if (index < array.length) {
            System.arraycopy(array, index, result, index + values.length, array.length - index);
        }
        return result;
    }

subarray方法

subarray 实质创建一个新数组,并把旧数组元素和要插入的元素copy到新数组相应的位置。然后返回新数组。

note

正常调用:采用了System.arraycopy(values, 0, result, index, values.length),这个和浅克隆的情况一样。任何对新旧数组相关的元素的修改都会影响另一方(基本数据类型、String类型除外)

源码

public static <T> T[] subarray(final T[] array, int startIndexInclusive, int endIndexExclusive) {
        if (array == null) {
            return null;
        }
        if (startIndexInclusive < 0) {
            startIndexInclusive = 0;
        }
        if (endIndexExclusive > array.length) {
            endIndexExclusive = array.length;
        }
        final int newSize = endIndexExclusive - startIndexInclusive;
        final Class<?> type = array.getClass().getComponentType();
        if (newSize <= 0) {
            @SuppressWarnings("unchecked") // OK, because array is of type T
            final T[] emptyArray = (T[]) Array.newInstance(type, 0);
            return emptyArray;
        }
        @SuppressWarnings("unchecked") // OK, because array is of type T
        final
        T[] subarray = (T[]) Array.newInstance(type, newSize);
        System.arraycopy(array, startIndexInclusive, subarray, 0, newSize);
        return subarray;
    }

add And addAll方法

这两个方法让java实现数组类型也可以像List等集合类一样的add操作,但是add方法要比addAll消耗性能。

  • add:每添加一个元素,就要Array.newInstance(array.getClass().getComponentType(), arrayLength + 1)创建一个新数组。然后System.arraycopy(array, 0, newArray, 0, arrayLength)copy一次。
  • addAll:只创建一次,copy一次。

源码

add

private static Object copyArrayGrow1(final Object array, final Class<?> newArrayComponentType) {
        if (array != null) {
            final int arrayLength = Array.getLength(array);
            final Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1);
            System.arraycopy(array, 0, newArray, 0, arrayLength);
            return newArray;
        }
        return Array.newInstance(newArrayComponentType, 1);
    }
public static <T> T[] add(final T[] array, final T element) {
        Class<?> type;
        if (array != null) {
            type = array.getClass().getComponentType();
        } else if (element != null) {
            type = element.getClass();
        } else {
            throw new IllegalArgumentException("Arguments cannot both be null");
        }
        @SuppressWarnings("unchecked") // type must be T
        final
        T[] newArray = (T[]) copyArrayGrow1(array, type);
        newArray[newArray.length - 1] = element;
        return newArray;
    }

addAll

public static <T> T[] addAll(final T[] array1, final T... array2) {
        if (array1 == null) {
            return clone(array2);
        } else if (array2 == null) {
            return clone(array1);
        }
        final Class<?> type1 = array1.getClass().getComponentType();
        @SuppressWarnings("unchecked") // OK, because array is of type T
        final T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length);
        System.arraycopy(array1, 0, joinedArray, 0, array1.length);
        try {
            System.arraycopy(array2, 0, joinedArray, array1.length, array2.length);
        } catch (final ArrayStoreException ase) {
            // Check if problem was due to incompatible types
            /*
             * We do this here, rather than before the copy because:
             * - it would be a wasted check most of the time
             * - safer, in case check turns out to be too strict
             */
            final Class<?> type2 = array2.getClass().getComponentType();
            if (!type1.isAssignableFrom(type2)) {
                throw new IllegalArgumentException("Cannot store " + type2.getName() + " in an array of "
                        + type1.getName(), ase);
            }
            throw ase; // No, so rethrow original
        }
        return joinedArray;
    }

contains方法

see indexOf 方法

indexOf方法

在使用indexOf方法时,对于自定义对象的index,需要实现equal方法,底层使用的equal方法。

 public static int indexOf(final Object[] array, final Object objectToFind, int startIndex) {
        if (array == null) {
            return INDEX_NOT_FOUND;
        }
        if (startIndex < 0) {
            startIndex = 0;
        }
        if (objectToFind == null) {
            for (int i = startIndex; i < array.length; i++) {
                if (array[i] == null) {
                    return i;
                }
            }
        } else {
            for (int i = startIndex; i < array.length; i++) {
                if (objectToFind.equals(array[i])) {
                    return i;
                }
            }
        }
        return INDEX_NOT_FOUND;
    }

isSorted方法

这个方法逻辑,就遍历数组,前后两两比较大小。
对于自定义对象,需要自定义实现Comparator接口。

源码

public static <T> boolean isSorted(final T[] array, final Comparator<T> comparator) {
        if (comparator == null) {
            throw new IllegalArgumentException("Comparator should not be null.");
        }

        if (array == null || array.length < 2) {
            return true;
        }

        T previous = array[0];
        final int n = array.length;
        for (int i = 1; i < n; i++) {
            final T current = array[i];
            if (comparator.compare(previous, current) > 0) {
                return false;
            }

            previous = current;
        }
        return true;
    }

lastIndexOf方法

和indexOf方法异曲同工,一个从前往后,一个是从后往前。

reverse方法

数组反转,两个指针,一个从前往后,一个从后往前,每次两个元素交换位置。

public static void reverse(final Object[] array, final int startIndexInclusive, final int endIndexExclusive) {
        if (array == null) {
            return;
        }
        int i = startIndexInclusive < 0 ? 0 : startIndexInclusive;
        int j = Math.min(array.length, endIndexExclusive) - 1;
        Object tmp;
        while (j > i) {
            tmp = array[j];
            array[j] = array[i];
            array[i] = tmp;
            j--;
            i++;
        }
    }

remove

这个是一系列方法,为数组提供像集合一样的操作,它包括:

  • 根据某个index 删除private static Object remove(final Object array, final int index)
  • 根据多个index,批量删除static Object removeAll(final Object array, final int... indices)
  • 根据值删除,删除所有的值public static <T> T[] removeAllOccurences(final T[] array, final T element);这里需要注意的是,这个会首先调用indexOf查找该值所在的index,若存在,然后在根据index删除,所以需要自定义实现equal方法
  • 根据值删除,只删除第一个出现的值 public static <T> T[] removeElement(final T[] array, final Object element),所以需要自定义实现equal方法
  • 根据值删除,只删除第一个出现的元素,但是这个还有不同;请看源码分析public static <T> T[] removeElements(final T[] array, final T... values)

源码

public static <T> T[] removeElements(final T[] array, final T... values) {
        if (isEmpty(array) || isEmpty(values)) {
            return clone(array);
        }
        //首先定义一个map存储要删除的元素和出现的次数
        final HashMap<T, MutableInt> occurrences = new HashMap<>(values.length);
        for (final T v : values) {
            final MutableInt count = occurrences.get(v);
            if (count == null) {
                occurrences.put(v, new MutableInt(1));
            } else {
                count.increment();
            }
        }
        //存储要删除元素在array中的index
        final BitSet toRemove = new BitSet();
        //遍历array查找元素这里用到的HashMap
        //而HashMap的get()方法底层也是通过HashCode方法和equal方法来确定唯一key,
        //所以对于自定义对象需要自己实现HashCode方法和equal
        for (int i = 0; i < array.length; i++) {
            final T key = array[i];
            final MutableInt count = occurrences.get(key);
            if (count != null) {
                if (count.decrementAndGet() == 0) {
                    occurrences.remove(key);
                }
                toRemove.set(i);
            }
        }
        @SuppressWarnings("unchecked") 
        // removeAll() always creates an array of the same type as its input
        final T[] result = (T[]) removeAll(array, toRemove);
        return result;
}

实例:
定义一个Person类

Person.java

public class Person {

    private String id;

    private String name;

    private int age;


    public Person(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        return true;
    }

    @Override
    public int hashCode() {
        return 1;
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws Exception {

        Person[] b = new Person[]{new Person("a","nsh",23)};

        Person a = new Person("a","nsh",23);

        Person[]c = ArrayUtils.removeElements(b,a);
        //c的长度为0,说明a确实被删除
        //如果把Person的hashCode方法去掉,这个是的c还是有一个元素。说明没有被删除,
        //明明a对象和b数组的元素的各个属性值一模一样为什么,没有匹配上删除掉呢,
        //这里就是刚刚提到的hashmap的hash计算和equql的原因。
        System.out.println(c.length);
    }

}

shift 方法

这个方法就很有意思
变换顺序,如:

public class Main {
    public static void main(String[] args) throws Exception {
    
        Integer[] arr = new Integer[]{1,2,3,4,5,6,7};
        ArrayUtils.shift(arr,2,5,1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
        System.out.println();
        Integer[] arrb = new Integer[]{1,2,3,4,5,6,7};
        ArrayUtils.shift(arr,1);
        for (int i = 0; i < arr.length; i++) {
            System.out.print(arr[i]);
        }
    }
}

结果

1253467
7125346

shuffle方法

打乱数组顺序,通过随机数实现

LazyInitializer

延迟加载:有时,应用程序必须仅在某些情况下处理对象,例如当用户选择特定菜单项或收到特殊事件时。如果对象的创建成本高或者内存或其他系统资源的消耗很大,那么推迟创建此对象直到真正需要它才有意义。这是延迟初始化模式的用例。

使用了懒汉模式:线程安全的懒汉模式,该类可以实现单例的懒汉式加载

实例代码

Lazy.java

public class Lazy extends LazyInitializer<Lazy> {
    @Override
    protected Lazy initialize() throws ConcurrentException {
        return init();
    }

    public Lazy(){}

    private Lazy init(){
        Lazy lazy = new Lazy();
        //do something

        return lazy;
    }
}

Main.java

public class Main {
    public static void main(String[] args) throws Exception {
        //不管调用了几次new Lazy().get()获取的都是同一个对象.
        //而new Lazy()这个对象并没有初始化操作
        //这个地方是可以优化,为了避免每次获取真正的对象,都要new Lazy()
        //可以设置静态变量,每次通过静态变量来获取真正的实例。
        Lazy lazy = new Lazy().get();
    }

}

SerializationUtils

clone方法

通过ByteArrayInputStream实现深克隆。

public static <T extends Serializable> T clone(final T object) {
        if (object == null) {
            return null;
        }
        final byte[] objectData = serialize(object);
        final ByteArrayInputStream bais = new ByteArrayInputStream(objectData);

        try (ClassLoaderAwareObjectInputStream in = new ClassLoaderAwareObjectInputStream(bais,
                object.getClass().getClassLoader())) {
            /*
             * when we serialize and deserialize an object,
             * it is reasonable to assume the deserialized object
             * is of the same type as the original serialized object
             */
            @SuppressWarnings("unchecked") // see above
            final T readObject = (T) in.readObject();
            return readObject;

        } catch (final ClassNotFoundException ex) {
            throw new SerializationException("ClassNotFoundException while reading cloned object data", ex);
        } catch (final IOException ex) {
            throw new SerializationException("IOException while reading or closing cloned object data", ex);
        }
    }

deserialize方法

反序列化方法

public static <T> T deserialize(final byte[] objectData) {
        Validate.isTrue(objectData != null, "The byte[] must not be null");
        return deserialize(new ByteArrayInputStream(objectData));
}
 public static <T> T deserialize(final InputStream inputStream) {
        Validate.isTrue(inputStream != null, "The InputStream must not be null");
        try (ObjectInputStream in = new ObjectInputStream(inputStream)) {
            @SuppressWarnings("unchecked")
            final T obj = (T) in.readObject();
            return obj;
        } catch (final ClassNotFoundException | IOException ex) {
            throw new SerializationException(ex);
        }
    }

serialize方法

序列化

public static byte[] serialize(final Serializable obj) {
        final ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
        serialize(obj, baos);
        return baos.toByteArray();
}
public static void serialize(final Serializable obj, final OutputStream outputStream) {
        Validate.isTrue(outputStream != null, "The OutputStream must not be null");
        try (ObjectOutputStream out = new ObjectOutputStream(outputStream)) {
            out.writeObject(obj);
        } catch (final IOException ex) {
            throw new SerializationException(ex);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

风吹千里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值