Java之Arrays工具类

JDK 8

---

Arrays工具类 是 JDK中 操作数组 的一个工具类,,本文展示 其常用的一些 public函数 的使用,涉及 复制数组、填充数组、排序、查找 等。

 

打印小工具:

private static Consumer<Object> cs = System.out::println;

 

目录

asList

toSting

sort

fill

setAll

copyOf,copyOfRange

binarySearch

stream

parallelPrefix

parallelSetAll

parallelSort

其它常用函数

 

低频函数

 

高频函数:

asList

多个同类型参数转换为list。

数组转换为List。

建立一个 ArrayList 返回。

	public static void testAsList() {
		// asList(T...)
		cs.accept("------测试asList:");
		List<Integer> list1 = Arrays.asList(23);
		cs.accept(list1);
		list1 = Arrays.asList(23,3,5,-12,345,424242);
		cs.accept(list1);
		
//		List<String> liste  = Arrays.asList(null); // NullPointerException
//		cs.accept(liste);
		
		List<String> list2 = Arrays.asList("apple", "tesla", "baidu", "ibm", "google", null);
		cs.accept(list2);
		
		Integer[] iarr1 = {2,3,4,1234,6,78,-23,90};
		List ilist = Arrays.asList(iarr1);
		cs.accept(ilist);
	}

结果:

------测试asList:
[23]
[23, 3, 5, -12, 345, 424242]
[apple, tesla, baidu, ibm, google, null]
[2, 3, 4, 1234, 6, 78, -23, 90]

 

注:ArrayList 转换为 数组呢?

调用其 toArray 方法——2个!

ArrayList 底层是用 数组来存储元素的。

transient Object[] elementData;

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

public <T> T[] toArray(T[] a) {
//...
System.arraycopy(elementData, 0, a, 0, size);
}

 

toString

使用 各个toString函数,可以输出 数组中的内容。

	public static void testToString() {
		cs.accept("------测试toString:");
		cs.accept("iarr1:");
		int[] iarr1 = {23, 3, 5, -12, 345, 424242, -234};
		cs.accept(iarr1);
		cs.accept(Arrays.toString(iarr1));

		cs.accept("iarr2:");
		Integer[] iarr2 = {23, 3, 5, -12, 345, 424242, -234};
		cs.accept(iarr2);
		cs.accept(Arrays.toString(iarr2));
		
		cs.accept("sarr1:");
		String[] sarr1 = {"apple", "tesla", "baidu", "ibm", "google", null};
		cs.accept(sarr1);
		cs.accept(Arrays.toString(sarr1));
	}

结果:

------测试toString:
iarr1:
[I@816f27d
[23, 3, 5, -12, 345, 424242, -234]
iarr2:
[Ljava.lang.Integer;@87aac27
[23, 3, 5, -12, 345, 424242, -234]
sarr1:
[Ljava.lang.String;@3e3abc88
[apple, tesla, baidu, ibm, google, null]

 

sort

1、非对象

使用 DualPivotQuicksort 算法,by Vladimir Yaroslavskiy

2、对象

配置了 LegacyMergeSort.userRequested,使用 legacyMergeSort;

否则,使用 ComparableTimSort 算法,基于 TimSort  算法;

--

无Comparator 参数的sort,对象数组中不能有 null!

	/**
	 * sort(byte[])
	 * sort(byte[], int, int)
	 * sort(T[], Comparator<? super T>)
	 * sort(T[], int, int, Comparator<? super T>)
	 * @author ben
	 * @date 2021-08-11 13:17:15 CST
	 */
	public static void testSort() {
		cs.accept("------测试sort:");
		// 基本的数据类型,底层使用 DualPivotQuicksort 算法
		int[] iarr1 = {23, 3, 5, -12, 345, 424242, -234};
		cs.accept("iarr1-int 排序前:");
		cs.accept(Arrays.toString(iarr1));
		Arrays.sort(iarr1);
		cs.accept("排序后:");
		cs.accept(Arrays.toString(iarr1));
		
		// 包装器类型,实际是对象
		// 底层使用 ComparableTimSort 算法
//		Integer[] iarr2 = {23, 3, 5, -12, 345, 424242, -234, null};
		Integer[] iarr2 = {23, 3, 5, -12, 345, 424242, -234};
		cs.accept("iarr2-Integer 排序前:");
		cs.accept(Arrays.toString(iarr2));
		Arrays.sort(iarr2); // 数组存在 null,抛出 NullPointerException
		cs.accept("排序后:");
		cs.accept(Arrays.toString(iarr2));
		
		// 字符串,就是 对象,同上面的 Integer数组
//		String[] sarr1 = {"apple", "tesla", "baidu", "ibm", "google", null};
		String[] sarr1 = {"apple", "tesla", "baidu", "ibm", "Google"};
		cs.accept("sarr1 排序前:");
		cs.accept(Arrays.toString(sarr1));
		Arrays.sort(sarr1); // 数组存在 null,抛出 NullPointerException
		cs.accept("排序后:");
		cs.accept(Arrays.toString(sarr1));
		
		// sort(T[], Comparator<? super T>)
		String[] sarr2 = {"apple", "tesla", "baidu", null, "ibm", "alibaba", "Google", "x", null};
		cs.accept("sarr2 排序前:");
		cs.accept(Arrays.toString(sarr2));
		Arrays.sort(sarr2, (s1,s2)->{
			// 按照字符串长度排序:短的在前,有null时,null在前
			// 支持 null
			if (Objects.equals(s1, s2)) {
				return 0;
			}
			
			if (s1 == null && s2 != null) {
				return -1;
			}
			
			if (s1 != null && s2 == null) {
				return 1;
			}
			
			return s1.length() - s2.length();
			
		});
		cs.accept("排序后:");
		cs.accept(Arrays.toString(sarr2));
	}

结果:

------测试sort:
iarr1-int 排序前:
[23, 3, 5, -12, 345, 424242, -234]
排序后:
[-234, -12, 3, 5, 23, 345, 424242]
iarr2-Integer 排序前:
[23, 3, 5, -12, 345, 424242, -234]
排序后:
[-234, -12, 3, 5, 23, 345, 424242]
sarr1 排序前:
[apple, tesla, baidu, ibm, Google]
排序后:
[Google, apple, baidu, ibm, tesla]
sarr2 排序前:
[apple, tesla, baidu, null, ibm, alibaba, Google, x, null]
排序后:
[null, null, x, ibm, apple, tesla, baidu, Google, alibaba]

 

疑问

对象数组中的 null 怎么一次性删除?有没有什么 “现成的工具类方法”?

 

fill

填充同一个数据,可以指定范围。

注意 和 setAll 区分!

	public static void testFill() {
		cs.accept("------测试fill:");
		cs.accept("barr:");
		boolean[] barr = new boolean[10];
		cs.accept(Arrays.toString(barr));
		cs.accept("barr after fill:");
		Arrays.fill(barr, true);
		cs.accept(Arrays.toString(barr));
		cs.accept("barr after fill 2:");
		Arrays.fill(barr, 0, 5, false);
		cs.accept(Arrays.toString(barr));
//		Arrays.fill(barr, -1, 5, false); // ArrayIndexOutOfBoundsException

		cs.accept("barr2:");
		Boolean[] barr2 = new Boolean[10];
		cs.accept(Arrays.toString(barr2));
		cs.accept("barr2 after fill:");
		Arrays.fill(barr2, true);
		cs.accept(Arrays.toString(barr2));
		cs.accept("barr2 after fill 2:");
		Arrays.fill(barr2, 5, barr2.length, false);
		cs.accept(Arrays.toString(barr2));
//		Arrays.fill(barr2, 5, 23, false); // ArrayIndexOutOfBoundsException
		
	}

结果:

------测试fill:
barr:
[false, false, false, false, false, false, false, false, false, false]
barr after fill:
[true, true, true, true, true, true, true, true, true, true]
barr after fill 2:
[false, false, false, false, false, true, true, true, true, true]
barr2:
[null, null, null, null, null, null, null, null, null, null]
barr2 after fill:
[true, true, true, true, true, true, true, true, true, true]
barr2 after fill 2:
[true, true, true, true, true, false, false, false, false, false]

 

setAll

按照数组元素序号(从0开始) 填充数据;

不需要序号的话,可以 设置第二个参数 返回其它值,比如,随机数。

	public static void testSetAll() {
		cs.accept("------测试setAll:");
		double[] dbarr = new double[10];
//		Double[] dbarr = new Double[10]; // java.lang.ArrayStoreException: java.lang.Integer
		cs.accept("dbarr:");
		cs.accept(Arrays.toString(dbarr));
		Arrays.setAll(dbarr, (ival)->{
			return ival * 10;
		});
		cs.accept("setAll之后:");
		cs.accept(Arrays.toString(dbarr));
		
		Double[] dbarr2 = new Double[10];
		cs.accept("\ndbarr2:");
		cs.accept(Arrays.toString(dbarr2));
		Arrays.setAll(dbarr2, (ival)->{
			// 转换后无异常
			return Double.valueOf(ival) * 10;
		});
		cs.accept("setAll之后:");
		cs.accept(Arrays.toString(dbarr2));
	}

结果:

------测试setAll:
dbarr:
[0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
setAll之后:
[0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]

dbarr2:
[null, null, null, null, null, null, null, null, null, null]
setAll之后:
[0.0, 10.0, 20.0, 30.0, 40.0, 50.0, 60.0, 70.0, 80.0, 90.0]

 

copyOf,copyOfRange

根据已有数组,建立新数组;

新数组的 size,可以 更小 或 更大;

更大时,使用 默认值填充——对象的默认值为 null。

	public static void testCopyOf() {
		cs.accept("------测试copyOf:");
		int[] iarr1 = new int[5];
		cs.accept("iarr1.length=" + iarr1.length);
		cs.accept(Arrays.toString(iarr1));
		// 增大
		int[] iarr2 = Arrays.copyOf(iarr1, 10);
		cs.accept("after copyOf: \niarr2.length=" + iarr2.length);
		cs.accept(Arrays.toString(iarr2));
		// 减小
		int[] iarr3 = Arrays.copyOf(iarr1, 3);
		cs.accept("after copyOf: \niarr3.length=" + iarr3.length);
		cs.accept(Arrays.toString(iarr3));
		
		// 指定范围copy
		String[] sarr1 = {"a", "bc", "def", "ghij", "klmno"};
		cs.accept("\nsarr1.length=" + sarr1.length);
		cs.accept(Arrays.toString(sarr1));
		// 新数组长度减小
		String[] sarr2 = Arrays.copyOfRange(sarr1, 2, sarr1.length);
		cs.accept("sarr2.length=" + sarr2.length);
		cs.accept(Arrays.toString(sarr2));
		// 新数组长度增大:新元素的值为 初始值——对象就为null
		String[] sarr3 = Arrays.copyOfRange(sarr1, 2, sarr1.length + 5);
		cs.accept("sarr3.length=" + sarr3.length);
		cs.accept(Arrays.toString(sarr3));
	}

结果:

------测试copyOf:
iarr1.length=5
[0, 0, 0, 0, 0]
after copyOf: 
iarr2.length=10
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
after copyOf: 
iarr3.length=3
[0, 0, 0]

sarr1.length=5
[a, bc, def, ghij, klmno]
sarr2.length=3
[def, ghij, klmno]
sarr3.length=8
[def, ghij, klmno, null, null, null, null, null]

 

binarySearch

底层都调用了 binarySearch0 函数——二分法检索 算法。

找到了,返回 序号;

没有找到,返回 最后计算的 -(low + 1)——源码如是说。

	public static void testBinarySearch() {
		cs.accept("------测试binarySearch:");
		int[] iarr1 = {23, 3, 5, -12, 345, 424242, -234};
		Arrays.sort(iarr1);
		cs.accept("iarr1 排序后:\n" + Arrays.toString(iarr1));
		cs.accept("find 3: " + Arrays.binarySearch(iarr1, 3));
		cs.accept("find 2: " + Arrays.binarySearch(iarr1, 2));
		cs.accept("find 20: " + Arrays.binarySearch(iarr1, 20));
		cs.accept("find 20: " + Arrays.binarySearch(iarr1, 20000));
		
		Integer[] iarr2 = {23, 3, 5, -12, 345, 424242, -234};
		Arrays.sort(iarr2);
		cs.accept("iarr2 排序后:\n" + Arrays.toString(iarr2));
		cs.accept("find 3: " + Arrays.binarySearch(iarr2, 3));
		cs.accept("find 2: " + Arrays.binarySearch(iarr2, 2));
//		cs.accept("find 3: " + Arrays.binarySearch(iarr2, null)); // NullPointerException
		
        // 未演示 Comparator版本,需要结合 sort使用——同一个 Comparator
	}

结果:

------测试binarySearch:
iarr1 排序后:
[-234, -12, 3, 5, 23, 345, 424242]
find 3: 2
find 2: -3
find 20: -5
find 20: -7
iarr2 排序后:
[-234, -12, 3, 5, 23, 345, 424242]
find 3: 2
find 2: -3

 

stream

将数组转换为 流对象,再进行 流处理。

	public static void testStream() {
		cs.accept("------测试Stream:");
		cs.accept("iarr1:");
		int[] iarr1 = {23, 3, 5, -12, 345, 424242, -234};
		IntStream iarr1Stream = Arrays.stream(iarr1);
		iarr1Stream.forEach(System.out::println);
		// 再次调用异常,因为流被关闭了
		// java.lang.IllegalStateException: stream has already been operated upon or closed
//		iarr1Stream.forEach(System.out::println);
		
		// 对象 to 流
		cs.accept("iarr2:");
		Integer[] iarr2 = {23, 3, 5, -12, 345, 424242, -234};
		Stream<Integer> iarr2Stream = Arrays.stream(iarr2);
		iarr2Stream.forEach(System.out::println);
	}

结果:

------测试Stream:
iarr1:
23
3
5
-12
345
424242
-234
iarr2:
23
3
5
-12
345
424242
-234

 

注:IntStream、LongStream、DoubleStream 需要探究下。

 

parallelPrefix

使用并行方式 做prefix计算,会改变 数组——第一个值必变,其它值为 根据 第二个 BinaryOperator 接口对象 计算的结果。

	public static void testParallelPrefix() {
		cs.accept("------测试parallelPrefix:");

		int[] iarr1 = {23, 3, 5, -12, 345, 424242, -234};
		cs.accept(Arrays.toString(iarr1));
		Arrays.parallelPrefix(iarr1, (i1, i2)->{
			return i1 + i2;
		});
		cs.accept("执行 parallelPrefix(i1 + i2) 后:");
		cs.accept(Arrays.toString(iarr1));
		
	}

结果:

------测试parallelPrefix:
[23, 3, 5, -12, 345, 424242, -234]
执行 parallelPrefix(i1 + i2) 后:
[23, 26, 31, 19, 364, 424606, 424372]

 

parallelSetAll

并性版的setAll,可以和 setAll比较下性能,千万、上亿级别长度时。

	public static void testParallelSetAll() {
		cs.accept("------测试parallelSetAll:");
		int[] iarr1 = new int[10];
		cs.accept(Arrays.toString(iarr1));
		Arrays.parallelSetAll(iarr1, (ival)->{
			return ival;
		});
		cs.accept("执行 parallelSetAll 后:");
		cs.accept(Arrays.toString(iarr1));
	}

结果:

------测试parallelSetAll:
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
执行 parallelSetAll 后:
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

parallelSort

并行方式排序。

底层使用 ForkJoinPool、TimSort、ArraysParallelSortHelpers 等实现排序。

比较了 100万、1000万、1亿 长度的数组的 sort和parallelSort 的性能,parallelSort在 1000+ 级别优胜。

	public static void testParallelSort() {
		cs.accept("------测试parallelSort:");

		Random rand = new Random(System.currentTimeMillis());
		int[] iarr1 = new int[1_000_000];
		Arrays.setAll(iarr1, (ival)->{
			return rand.nextInt(2000);
		});
		
		int[] iarr2 = Arrays.copyOf(iarr1, iarr1.length);
		
//		cs.accept("排序前:iarr1=\n" + Arrays.toString(iarr1));
//		cs.accept("排序前:iarr2=\n" + Arrays.toString(iarr2));
		cs.accept("");
		
		long s1 = System.nanoTime();
		Arrays.parallelSort(iarr1);
		long s2 = System.nanoTime();
//		cs.accept("排序parallelSort后:iarr1=\n" + Arrays.toString(iarr1));
		cs.accept("parallelSort耗时:\n" + (s2-s1) + "ns\n");
		
		long s3 = System.nanoTime();
		Arrays.sort(iarr2);
		long s4 = System.nanoTime();
//		cs.accept("排序sort后:iarr2=\n" + Arrays.toString(iarr2));
		cs.accept("sort耗时:\n" + (s4-s3) + "ns");
		
	}

结果:

------测试parallelSort:

parallelSort耗时:
157193900ns

sort耗时:
46495500ns

上面测试是的 100万 数据的数组,此时,sort的性能优于 parallelSort。

将数组长度变更为 1000万、1亿测试,结果分别如下:

------测试parallelSort:
# 1000万:parallelSort 领先
parallelSort耗时:
296669600ns

sort耗时:
414690000ns

------测试parallelSort:
# 1亿:parallelSort 领先更多了
parallelSort耗时:
1501550900ns

sort耗时:
4807463200ns

注:上面测试的电脑CPU为8核心(i5-8250U)。

 

其它常用函数

hashCode

deepHashCode

equals

deepEquals

deepToString

 

低频函数

spliterator

做什么用的呢?

 

后记:

Arrays很强大,作者如下:

 * @author Josh Bloch
 * @author Neal Gafter
 * @author John Rose
 * @since  1.2
 */
public class Arrays {

 

posted @ 2021-08-11 16:50  快乐的欧阳天美1114  阅读(119)  评论(0编辑  收藏  举报