本系列的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到新数组相应的位置。然后返回新数组。
这里需要注意的是
ArrayUtils.insert(index, array, null)= cloned copy of 'array'
这种调用情况的clone是浅克隆,任何对新旧数组相关的元素的修改都会影响另一方(基本数据类型、String类型除外)- 正常调用:采用了
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);
}
}