【小家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迭代器的时候专题讲解



相关文章
|
7天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
35 17
|
3天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
28 4
|
4天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
12 2
|
8天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
12天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
12天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
12天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
20 3
|
15天前
|
Java 程序员 测试技术
Java|让 JUnit4 测试类自动注入 logger 和被测 Service
本文介绍如何通过自定义 IDEA 的 JUnit4 Test Class 模板,实现生成测试类时自动注入 logger 和被测 Service。
20 5
|
15天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
27 2
|
15天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
27 2