• 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("
    dbarr2:");
    		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: 
    iarr2.length=" + iarr2.length);
    		cs.accept(Arrays.toString(iarr2));
    		// 减小
    		int[] iarr3 = Arrays.copyOf(iarr1, 3);
    		cs.accept("after copyOf: 
    iarr3.length=" + iarr3.length);
    		cs.accept(Arrays.toString(iarr3));
    		
    		// 指定范围copy
    		String[] sarr1 = {"a", "bc", "def", "ghij", "klmno"};
    		cs.accept("
    sarr1.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 排序后:
    " + 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 排序后:
    " + 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=
    " + Arrays.toString(iarr1));
    //		cs.accept("排序前:iarr2=
    " + Arrays.toString(iarr2));
    		cs.accept("");
    		
    		long s1 = System.nanoTime();
    		Arrays.parallelSort(iarr1);
    		long s2 = System.nanoTime();
    //		cs.accept("排序parallelSort后:iarr1=
    " + Arrays.toString(iarr1));
    		cs.accept("parallelSort耗时:
    " + (s2-s1) + "ns
    ");
    		
    		long s3 = System.nanoTime();
    		Arrays.sort(iarr2);
    		long s4 = System.nanoTime();
    //		cs.accept("排序sort后:iarr2=
    " + Arrays.toString(iarr2));
    		cs.accept("sort耗时:
    " + (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 {
  • 相关阅读:
    basic-linux
    巧用border属性
    git常用操作笔记
    如何删除github里的项目
    常用css3属性的ie兼容查看
    新建pc端页面的模板
    HTML5 Shiv--解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
    进程和线程
    C++对象模型---第 4 章 Function语意学
    C++对象模型---第 3 章 Data语意学
  • 原文地址:https://www.cnblogs.com/luo630/p/15127717.html
Copyright © 2020-2023  润新知