【小家java】聊聊Java中的java.util.Arrays类和java.util.Collections工具类(中)

简介: 【小家java】聊聊Java中的java.util.Arrays类和java.util.Collections工具类(中)

compare和compareUnsigned


备注:此两个方法是JDK9提供的方法。JDK里没有哦,所以本文不介绍此方法


copyOf和copyOfRange 复制、截取数组


这个在复制数组和截取数组的时候非常有用,推荐使用:


 public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        //复制出新的数组,复制长度由 newLength 决定,长度可大于被复制数组的长度 也可以小于
        Integer[] copyArray1 = Arrays.copyOf(arrayTest, 2); //长度小于
        Integer[] copyArray11 = Arrays.copyOf(arrayTest, 15); //长度大于
        System.out.println(Arrays.toString(copyArray1)); //[6, 1]
        System.out.println(Arrays.toString(copyArray11)); //[6, 1, 9, 2, 5, 7, 6, 10, 6, 12, null, null, null, null, null]
        //复制指定下标范围内的值,含头不含尾(这是通用规则)
        Integer[] copyArray2 = Arrays.copyOfRange(arrayTest, 2, 7); //截取部分
        System.out.println(Arrays.toString(copyArray2));
    }

equals和deepEquals:比较数组内容是否相等


equals:比较一位数组内容

deepEquals:比较二维数组内容

  public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        //定义一个二维数组  学生成绩
        Integer[][] stuGrades = {{1, 3, 5, 7, 9}, {2, 4, 6, 8}, {1, 5, 10}};
        Integer[] equals1 = Arrays.copyOf(arrayTest, arrayTest.length); //完整拷贝
        //比较一维数组内容是否相等
        System.out.println(Arrays.equals(equals1, arrayTest)); //true
        Integer[][] equals2 = Arrays.copyOf(stuGrades, stuGrades.length); 
        //比较二维数组内容是否相等
        System.out.println(Arrays.deepEquals(stuGrades, equals2)); //true
    }

toString和deepToString

public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        //一维数组toString
        System.out.println(Arrays.toString(arrayTest));
        Integer[][] stuGrades={{1,3,5,7,9},{2,4,6,8},{1,5,10}};
        //二维数组toString
        System.out.println(Arrays.deepToString(stuGrades));
    }

hashCode和deepHashCode


基本同上,例子略


fill:将一个数组全部置为 val ,或在下标范围内将数组置为 val

  public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        //Integer[] arrayTest = new Integer[5];
        //将一个数组置为 val(5)
        Arrays.fill(arrayTest,5);
        System.out.println(Arrays.toString(arrayTest)); //[5, 5, 5, 5, 5, 5, 5, 5, 5, 5]
        //将一个数组指定范围内置为 val(10)  含头不含尾
        Arrays.fill(arrayTest,2,3,10); //通过角标 可以控制可以批量换值
        System.out.println(Arrays.toString(arrayTest)); //[5, 5, 10, 5, 5, 5, 5, 5, 5, 5]
    }


setAll和parallelSetAll:JDK有歧义的实现方式


这个有点类似于Stream里的Map,但是JDK的实现有bug。

setAll的语义,作者的元思想肯定是希望处理each element,但是我们看看源码:


public static <T> void setAll(T[] array, IntFunction<? extends T> generator) {
        Objects.requireNonNull(generator);
        for (int i = 0; i < array.length; i++)
            array[i] = generator.apply(i);
    }


generator.apply(i);它竟然用的是i,而不是array[i],所以此处我们使用的时候一定要万分注意,回调给我们的是角标,而不是值,而不是值


 public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        Integer[] setAllArr = Arrays.copyOf(arrayTest, arrayTest.length);
        //一个数组全部做表达式操作
        System.out.println(Arrays.toString(setAllArr)); //[6, 1, 9, 2, 5, 7, 6, 10, 6, 12]
        //一定要注意 此处我们需要用setAllArr[i]才是我们想要的结果
        Arrays.setAll(setAllArr, i -> setAllArr[i] * 3);
        //Arrays.setAll(setAllArr, i -> i * 3);
        System.out.println(Arrays.toString(setAllArr)); //[18, 3, 27, 6, 15, 21, 18, 30, 18, 36]
    }


看到这种使用方式,很逆天有没有。真不知道是JDK的设计者脑子进水了,还是进水了,因为处理角标没任何意义,至少我是不太同意这种设计的。


一次同样的parallelSetAll也是采用的角标,他们的区别只是一个串行,一个并行。


stream 这个就不解释了


parallelPrefix:二元迭代,对原数组内容进行二元操作


这个解释起来比较麻烦,直接先看一个例子吧


 public static void main(String[] args) {
        Integer[] arrayTest = {6, 1, 9, 2, 5, 7, 6, 10, 6, 12};
        Integer[] arrayPP1 = Arrays.copyOf(arrayTest, arrayTest.length);
        System.out.println(Arrays.toString(arrayPP1)); //[6, 1, 9, 2, 5, 7, 6, 10, 6, 12]
        //二元迭代,对原数组内容进行二元操作
        Arrays.parallelPrefix(arrayPP1, (x, y) -> x * y);
        System.out.println(Arrays.toString(arrayPP1)); //[6, 6, 54, 108, 540, 3780, 22680, 226800, 1360800, 16329600]
        //在指定下标范围内,对原数组内容进行二元操作,下标含头不含尾
        Integer[] arrayPP2 = Arrays.copyOf(arrayTest, arrayTest.length);
        Arrays.parallelPrefix(arrayPP2, 0, 5, (x, y) -> x * y);
        System.out.println(Arrays.toString(arrayPP2)); //[6, 6, 54, 108, 540, 7, 6, 10, 6, 12]
    }

从效果中我们可以看出来,这个就是拿前面的结果,和当前元素做运算。这个运算规则,由我们自己来定义。


使用场景:这个在一些数学运算中,会比较好用


sort和parallelSort:算法精华


排序一直以来效率是很依赖于算法的,所以我们抽查一个源码看看它的精妙之处:


static void sort(byte[] a, int left, int right) {
    // Use counting sort on large arrays
    if (right - left > COUNTING_SORT_THRESHOLD_FOR_BYTE) {
        int[] count = new int[NUM_BYTE_VALUES];
        for (int i = left - 1; ++i <= right;
            count[a[i] - Byte.MIN_VALUE]++
        );
        for (int i = NUM_BYTE_VALUES, k = right + 1; k > left; ) {
            while (count[--i] == 0);
            byte value = (byte) (i + Byte.MIN_VALUE);
            int s = count[i];
            do {
                a[--k] = value;
            } while (--s > 0);
        }
    } else { // Use insertion sort on small arrays
        for (int i = left, j = i; i < right; j = ++i) {
            byte ai = a[i + 1];
            while (ai < a[j]) {
                a[j + 1] = a[j];
                if (j-- == left) {
                    break;
                }
            }
            a[j + 1] = ai;
        }
    }
}


如果大于域值那么就使用计数排序法,否则就使用插入排序。


parallelSort是java8中新出的一种排序API,这是一种并行排序,Arrays.parallelSort使用了Java7的Fork/Join框架使排序任务可以在线程池中的多个线程中进行,Fork/Join实现了一种任务窃取算法,一个闲置的线程可以窃取其他线程的闲置任务进行处理。


性能对比结论:数组数据量越大,parallelSort的优势就越明显。


jdk源码中写的排序算法都很精简,值得学习

spliterator:最优的遍历


这是JDK为了高级遍历数组而提供的一个方法。具体使用方式,会在后续讲解spliterator迭代器的时候专题讲解



相关文章
|
5天前
|
Java 编译器 ice
【Java开发指南 | 第十五篇】Java Character 类、String 类
【Java开发指南 | 第十五篇】Java Character 类、String 类
24 1
|
3天前
|
自然语言处理 Java API
Java 8的Stream API和Optional类:概念与实战应用
【5月更文挑战第17天】Java 8引入了许多重要的新特性,其中Stream API和Optional类是最引人注目的两个。这些特性不仅简化了集合操作,还提供了更好的方式来处理可能为空的情况,从而提高了代码的健壮性和可读性。
26 7
|
5天前
|
Java
【Java开发指南 | 第十四篇】Java Number类及Math类
【Java开发指南 | 第十四篇】Java Number类及Math类
16 1
|
1天前
|
安全 Java 容器
Java一分钟之-并发编程:线程安全的集合类
【5月更文挑战第19天】Java提供线程安全集合类以解决并发环境中的数据一致性问题。例如,Vector是线程安全但效率低;可以使用Collections.synchronizedXxx将ArrayList或HashMap同步;ConcurrentHashMap是高效线程安全的映射;CopyOnWriteArrayList和CopyOnWriteArraySet适合读多写少场景;LinkedBlockingQueue是生产者-消费者模型中的线程安全队列。注意,过度同步可能影响性能,应尽量减少共享状态并利用并发工具类。
15 2
|
3天前
|
设计模式 算法 Java
Java的前景如何,好不好自学?,万字Java技术类校招面试题汇总
Java的前景如何,好不好自学?,万字Java技术类校招面试题汇总
|
5天前
|
安全 Java 开发者
Java一分钟之-文件与目录操作:Path与Files类
【5月更文挑战第13天】Java 7 引入`java.nio.file`包,`Path`和`Files`类提供文件和目录操作。`Path`表示路径,不可变。`Files`包含静态方法,支持创建、删除、读写文件和目录。常见问题包括:忽略异常处理、路径解析错误和权限问题。在使用时,注意异常处理、正确格式化路径和考虑权限,以保证代码稳定和安全。结合具体需求,这些方法将使文件操作更高效。
11 2
|
5天前
|
安全 Java 开发者
Java一分钟之-Optional类:优雅处理null值
【5月更文挑战第13天】Java 8的`Optional`类旨在减少`NullPointerException`,提供优雅的空值处理。本文介绍`Optional`的基本用法、创建、常见操作,以及如何避免错误,如直接调用`get()`、误用`if (optional != null)`检查和过度使用`Optional`。正确使用`Optional`能提高代码可读性和健壮性,建议结合实际场景灵活应用。
22 3
|
5天前
|
存储 Java 索引
【Java开发指南 | 第十六篇】Java数组及Arrays类
【Java开发指南 | 第十六篇】Java数组及Arrays类
10 3
|
5天前
|
存储 缓存 Java
【Java开发指南 | 第六篇】Java成员变量(实例变量)、 类变量(静态变量)
【Java开发指南 | 第六篇】Java成员变量(实例变量)、 类变量(静态变量)
12 2
|
5天前
|
Java 编译器
【Java开发指南 | 第一篇】类、对象基础概念及Java特征
【Java开发指南 | 第一篇】类、对象基础概念及Java特征
11 4