Wend看源码-Java-Arrays 工具集学习

摘要

java.util.Arrays 是 Java 标准库中的一个实用工具类,它提供了各种静态方法来操作数组,包括排序、搜索、比较、填充等。这些方法简化了对数组的操作,并且在很多情况下可以提高代码的可读性和效率。以下是关于Arrays类中提供的一些主要方法的总结。

Sort(排序)

对指定的数组进行升序排序。Collection接口和List 接口中的 sort 方法都是基于Arrays.sort方法实现的。 当你调用Arrays.sort()时,内部逻辑会自动选择最适合输入数据类型的排序算法:

  • 对于对象数组(实现了Comparable接口的对象),它使用ComparableTimSort
  • 对于带有显式比较器的对象数组,它也使用TimSort,但是通过提供比较器来进行元素间的比较。
  • 对于基本类型的数组(例如int[], double[]等),它使用DualPivotQuicksort。

示例代码

package person.wend.javalearnexample.util;

public class ArraysExample {

    public static void main(String[] args) {
        numberSort();
        objectSort();
    }

    public static void numberSort() {
        // 数字排序
        int[] numbers = { 3, 1, 4, 1, 5, 9, 2, 6 };
        java.util.Arrays.sort(numbers);
        for (int i : numbers) {
            System.out.print(i + " ");
        }
        System.out.println();
    }

    public static void objectSort(){
        // 对象排序
        String[] strings = { "Hello", "World", "Java" };
        java.util.Arrays.sort(strings);
        for (String s : strings) {
            System.out.print(s + " ");
        }
        System.out.println();
    }
}

排序算法 TimSort

TimSort是一种混合排序算法,源自合并排序和插入排序。它是由Tim Peters在2002年为Python编程语言发明的,并且后来被Java采用作为默认的排序算法(从Java 7开始)。TimSort旨在利用真实世界数据中常见的模式来提高性能。它首先识别出已经有序的数据段(称为"runs"),然后以一种高效的方式合并这些段。对于大多数实际数据集来说,TimSort的表现非常好,其平均和最坏情况下的时间复杂度都是O(n log n)。

ComparableTimSort

ComparableTimSort是TimSort的一个变体,专门用于处理实现了Comparable接口的对象数组或列表。这意味着数组中的元素能够自行比较大小,不需要额外提供比较器。Java的Arrays.sort(T[] a)List.sort(Comparator<? super T> c)(当没有提供自定义比较器时)使用这个变体。

排序算法 DualPivotQuicksort

DualPivotQuicksort是快速排序的一种变体,由Vladimir Yaroslavskiy提出,用于改进传统的单轴快速排序。与标准的快速排序不同,它选择了两个枢轴点而不是一个,从而将数组分为三个部分。这种算法通常比传统快速排序更有效率,特别是对于包含大量重复元素的数据集。从Java 7开始,Arrays.sort()对基本类型(如int、double等)使用了DualPivotQuicksort。

binarySearch(二分搜索)

Arrays.binarySearch()用于在一个已经排序的数组中执行二分查找(binary search)。该方法可以对基本类型数组和对象数组进行高效的搜索。对于对象数组,要求这些对象实现了 Comparable 接口或者提供了一个 Comparator 来定义元素之间的比较规则。

示例代码

public static void binarySearch() {
        // 创建一个整数数组
        int[] numbers = {3, 6, 2, 8, 4, 7, 5, 1};

        // 使用 binarySearch 查找元素 5
        int index = Arrays.binarySearch(numbers, 5);

        // 打印查找结果
        System.out.println("元素 5 的索引位置为:" + index);
    }

注意事项

  • 数组必须是已排序的。如果数组没有排序,那么结果是不确定的,可能会返回错误的结果。

  • 对于对象数组,数组中的元素类型必须实现Comparable接口,这样才能确定元素之间的大小关系,以便进行二分查找。如果没有实现Comparable接口,会在运行时抛出ClassCastException异常。

equals

Arrays.equals() 用于比较两个一维数组的内容是否相等。它会逐一比较两个数组中的元素,如果所有对应位置上的元素都相等,则返回 true;否则返回 false。对于多维数组,equals() 只比较顶层元素的引用,而不是递归地比较内部数组的内容。

deepEquals

Arrays.deepEquals() 方法不仅比较顶层元素,还会递归地比较多维数组中每个子数组的内容,直到所有的嵌套层级都被检查完毕。这对于包含其他数组的对象数组来说是非常有用的,因为它确保了整个结构的深度相等性。

示例代码

 public static void equalsTest(){
        int[] array1 = {1, 2, 3};
        int[] array2 = {1, 2, 3};
        int[] array3 = {3, 2, 1};

        // 比较两个相同内容的一维数组
        System.out.println("array1 and array2 are equal: " + Arrays.equals(array1, array2)); // true

        // 比较两个不同顺序的一维数组
        System.out.println("array1 and array3 are equal: " + Arrays.equals(array1, array3)); // false

        // 对于对象类型的数组,比较的是对象的内容(假设实现了正确的 equals 方法)
        String[] stringArray1 = {"hello", "world"};
        String[] stringArray2 = {"hello", "world"};

        System.out.println("stringArray1 and stringArray2 are equal: " + Arrays.equals(stringArray1, stringArray2)); // true


        Integer[][] array4 = {{1, 2}, {3, 4}};
        Integer[][] array5 = {{1, 2}, {3, 4}};
        Integer[][] array6 = {{1, 2}, {4, 3}};

        // 使用 Arrays.deepEquals() 比较两个相同内容的二维数组
        System.out.println("array1 and array2 are deeply equal: " + Arrays.deepEquals(array4, array5)); // true

        // 使用 Arrays.deepEquals() 比较两个不同内容的二维数组
        System.out.println("array1 and array3 are deeply equal: " + Arrays.deepEquals(array4, array6)); // false

        // 对于更复杂的数据结构,比如包含其他数组的对象数组
        Object[] complexArray1 = new Object[]{"hello", new Integer[]{1, 2}, new String[]{"a", "b"}};
        Object[] complexArray2 = new Object[]{"hello", new Integer[]{1, 2}, new String[]{"a", "b"}};

        System.out.println("complexArray1 and complexArray2 are deeply equal: " + Arrays.deepEquals(complexArray1, complexArray2)); // true
    }

fill

Arrays.fill() 方法用于将指定的值填充到整个数组或数组的一部分。它是一个非常方便的方法,可以快速初始化数组或更新现有数组的内容,而不需要使用循环来逐个设置元素。

示例代码

 public static void fillTest() {
        // 创建一个整型数组并用 7 填充
        int[] numbers = new int[5];
        Arrays.fill(numbers, 7);
        System.out.println("Filled array with 7: " + Arrays.toString(numbers)); // [7, 7, 7, 7, 7]

        // 创建一个字符串数组并用 "hello" 填充
        String[] greetings = new String[3];
        Arrays.fill(greetings, "hello");
        System.out.println("Filled string array: " + Arrays.toString(greetings)); // [hello, hello, hello]

        // 创建一个整型数组并部分填充
        int[] numbers2 = {1, 2, 3, 4, 5};
        Arrays.fill(numbers2, 1, 4, 8); // 从索引 1 到 3(不包括 4)的元素被替换为 8
        System.out.println("Partially filled array: " + Arrays.toString(numbers2)); // [1, 8, 8, 8, 5]

        // 创建一个字符数组并部分填充
        char[] chars = {'a', 'b', 'c', 'd', 'e'};
        Arrays.fill(chars, 2, 5, 'z'); // 从索引 2 到 4(不包括 5)的元素被替换为 'z'
        System.out.println("Partially filled char array: " + Arrays.toString(chars)); // [a, b, z, z, z]
    }

copyOf

Arrays.copyOf 方法用于复制指定的数组,截断或填充(如有必要),以使副本具有指定的长度。如果原数组的长度小于新数组的长度,则填充 null(对于对象数组)或相应的默认值(对于基本类型数组,比如 0、false 等)。如果原数组的长度大于新数组的长度,则只复制指定长度内的元素。

copyOfRange

Arrays.copyOfRange 方法用于复制指定数组的指定范围到新数组中。这个方法允许你指定开始索引(包含)和结束索引(不包含)来复制数组的一部分。

示例代码

public static void copyOfTest() {
        String[] original = {"Apple", "Banana", "Cherry", "Date"};
        String[] copy = Arrays.copyOf(original, 2); // 复制前两个元素
        System.out.println(Arrays.toString(copy)); // 输出: [Apple, Banana]

        String[] longerCopy = Arrays.copyOf(original, 6); // 原数组长度小于新长度,填充null
        System.out.println(Arrays.toString(longerCopy)); // 输出: [Apple, Banana, Cherry, Date, null, null]
        // copyOfRange
        String[] copyRange = Arrays.copyOfRange(original, 1, 3); // 复制索引1到索引2(不包括索引3)的元素
        System.out.println(Arrays.toString(copyRange)); // 输出: [Banana, Cherry]
    }

asList

Arrays.asList()用于将一个数组转换为一个固定大小的List集合。这个方法提供了一种便捷的方式,使得可以使用一些List接口的方法来操作数组元素。

示例代码

  • 基本数据类型数组转换:对于基本数据类型数组,如int[]double[]等,基本数据类型数组在作为可变参数传递时,被视为一个单独的对象。

    int[] intArray = {1, 2, 3};
    List<int[]> list = Arrays.asList(intArray);
    // 此时list中只有一个元素,就是intArray这个数组本身
    System.out.println(list.size()); // 输出1

  • 如果要将基本数据类型数组转换为包含基本数据类型元素的List,可以先将基本数据类型数组转换为对应的包装类型数组,例如:

    Integer[] integerArray = new Integer[intArray.length];
    for (int i = 0; i < intArray.length; i++) {
    integerArray[i] = intArray[i];
    }
    List<Integer> integerList = Arrays.asList(integerArray);
    System.out.println(integerList.size()); // 输出3

  • 对象数组转换示例:对于对象数组,使用就比较直接。例如,对于String数组:

    String[] stringArray = {"apple", "banana", "cherry"};
    List<String> stringList = Arrays.asList(stringArray);
    System.out.println(stringList.size()); // 输出3
    System.out.println(stringList.get(1)); // 输出banana

hashCode

Arrays.hashCode() 方法为单层数组计算哈希码。该方法会遍历数组中的每一个元素,并将这些元素的哈希码组合成一个单一的哈希值。这个过程确保了即使两个不同的数组实例包含相同的元素序列,它们也会有相同的哈希码,从而可以正确地用作哈希表中的键或集合中的成员。

deepHashCode

Arrays.deepHashCode() 方法则进一步处理多维数组,递归地计算每个子数组的哈希码。这对于包含其他数组的对象数组特别有用,因为它不仅考虑顶层元素,还深入到每一层嵌套的数组,确保整个结构的内容都被纳入哈希码的计算之中。

示例代码

 public static void hashCodeTest(){
        // 计算字符串数组的哈希码
        String[] array1 = {"apple", "banana", "cherry"};
        System.out.println("Hash code of array1: " + Arrays.hashCode(array1));

        // 对于基本类型数组,使用相应的 hashCode 方法
        int[] numbers = {1, 2, 3};
        System.out.println("Hash code of numbers: " + Arrays.hashCode(numbers));
        // 示例 2 deepHashCode
        // 创建一个二维字符串数组并计算其深哈希码
        String[][] array2D = {{"apple", "banana"}, {"cherry", "date"}};
        System.out.println("Deep hash code of 2D array: " + Arrays.deepHashCode(array2D));

        // 对于更复杂的数据结构,比如包含其他数组的对象数组
        Object[] complexArray = new Object[]{"hello", new Integer[]{1, 2}, new String[]{"a", "b"}};
        System.out.println("Deep hash code of complex array: " + Arrays.deepHashCode(complexArray));

    }

toString

toString 方法用于返回指定数组内容的字符串表示形式。该方法适用于任何类型的数组,包括基本类型数组和对象数组。

deepToString

deepToString 方法用于返回多维数组(即数组的数组)的字符串表示形式。当数组是嵌套的,toString 方法只能打印出最外层数组的引用,而 deepToString 能够递归地打印出所有层的元素。

示例代码

 public static void toStringTest(){
        int[] array = {1, 2, 3, 4, 5};
        String arrayString = Arrays.toString(array);
        System.out.println(arrayString); // 输出: [1, 2, 3, 4, 5]

        int[][] deepArray = {{1, 2}, {3, 4, 5}, {6}};
        String deepArrayString = Arrays.deepToString(deepArray);
        System.out.println(deepArrayString); // 输出: [[1, 2], [3, 4, 5], [6]]
    }

JDK 8 新增方法

parallelSort(并发排序)

Arrays.parallelSort 是 Java 8+ 标准库提供的一个方法,用于对数组进行并行排序。它利用了 Java 的 Fork/Join 框架来实现多线程并行处理,可以加速大数组的排序操作。与 Arrays.sort() 不同,parallelSort() 方法旨在充分利用多核处理器的能力来提高性能。

示例代码

import java.util.Arrays;

public class ParallelSortExample {
    public static void main(String[] args) {
        int[] array = {5, 2, 8, 1, 9, 3};

        // 使用 parallelSort() 对数组进行排序
        Arrays.parallelSort(array);

        // 输出排序后的数组
        System.out.println(Arrays.toString(array));
    }
}

ArraysParallelSortHelpers

这个类被用于辅助Arrays.parallelSort的实现,包含了用于并行排序的辅助方法和静态内部类。ArraysParallelSortHelpers 主要用于将排序任务分割成更小的任务,并在多个处理器核心上并行执行它们。

  1. 任务分割:ArraysParallelSortHelpers 将大数组分割成多个子数组,每个子数组可以由一个单独的线程进行排序。

  2. 任务合并:在子数组被排序后,ArraysParallelSortHelpers 提供了合并这些子数组的方法,以生成最终排序好的大数组。

  3. 工作窃取算法:为了提高效率,ArraysParallelSortHelpers 使用了工作窃取算法。这意味着空闲的线程可以从其他忙碌线程的任务队列中"窃取"任务。

parallelSort 与Sort 对比

  • 单线程 vs. 多线程:sort() 是一个单线程方法,而 parallelSort() 则会创建多个线程来进行并行排序。

  • 性能差异:对于较小的数组,sort() 可能比 parallelSort() 更快,因为后者需要额外的开销来设置和管理线程池。然而,对于较大的数组,parallelSort() 可以通过多线程并发执行来提供更好的性能。

  • 排序算法:sort() 对于原始类型(如 int, long 等)使用双轴快速排序算法,对于对象数组则使用 TimSort 算法。parallelSort() 在内部根据情况选择适当的算法,并且为了并行化可能会使用不同的策略。

  • 使用场景

    • sort():适用于小规模数据集或者当你的应用程序已经有很多活跃线程时,避免引入更多线程带来的额外开销。

    • parallelSort():适用于大规模数据集并且运行在多核处理器上的环境,能够从多线程中受益以减少排序时间。

parallelPrefix(前缀计算)

Arrays.parallelPrefix()方法是 Java 8 中引入的一个并行数组操作方法。它用于对给定的数组进行前缀计算,计算方式是基于一个指定的二元操作符。该方法可以利用多核处理器的并行计算能力,在合适的场景下提高计算效率。

工作原理

  • 假设我们有一个数组[a, b, c, d],在执行parallelPrefix操作时,对于二元操作符op,计算过程如下:

  • 首先,计算第一个元素和第二个元素的结果,即newArray[1]=op.apply(array[0], array[1]),新数组的第一个元素通常保持不变(取决于操作符)。

  • 然后,计算新的第二个元素和第三个元素的结果,即newArray[2]=op.apply(newArray[1], array[2])

  • 以此类推,最后得到一个经过前缀计算后的数组。例如,如果操作符是加法,对于数组[1, 2, 3, 4],计算后的数组为[1, 1 + 2, (1 + 2)+3, ((1 + 2)+3)+4],即[1, 3, 6, 10]

示例代码

 public static void parallelPrefixTest(){
        // 创建一个整数数组
        int[] numbers = {1, 2, 3, 4, 5};

        // 使用 parallelPrefix 计算数组的前缀和
        Arrays.parallelPrefix(numbers, (left, right) -> left + right);

        // 打印计算结果
        System.out.println(Arrays.toString(numbers));
    }

setAll(设置新值)

Arrays.setAll() 方法使用单线程来顺序地为数组中的每一个位置设置一个新值。它接受一个数组和一个 IntUnaryOperator 接口的实例作为参数,后者定义了一个操作,该操作接收一个整数参数(即数组索引)并返回一个新的整数值来填充对应的数组位置。

parallelSetAll(并行设置新值)

Arrays.parallelSetAll() 方法与 setAll() 类似,但它利用 Java 的 Fork/Join 框架来并行化任务执行,从而加速大数组的填充过程。这个方法会尝试将数组分割成多个部分,并在不同的线程上同时应用生成器函数来填充这些部分。对于小数组或者只有一个处理器核心的情况下,parallelSetAll() 可能不会提供性能优势,甚至可能比 setAll() 更慢,因为并行处理引入了额外的开销。

示例代码

public static void setAllExample(){
        // 使用 setAll 初始化一个整型数组,每个元素是其索引的平方
        int[] squares = new int[5];
        Arrays.setAll(squares, i -> i * i);
        System.out.println("Squares: " + Arrays.toString(squares)); // [0, 1, 4, 9, 16]

        // 使用 setAll 初始化一个字符串数组,每个元素是其索引的字符表示
        String[] indexedStrings = new String[3];
        Arrays.setAll(indexedStrings, Integer::toString);
        System.out.println("Indexed strings: " + Arrays.toString(indexedStrings)); // [0, 1, 2]

        // 使用 parallelSetAll 并行初始化一个大整型数组,每个元素是其索引的立方
        int[] cubes = new int[1000000];
        Arrays.parallelSetAll(cubes, i -> i * i * i);

        // 打印前五个元素作为示例
        System.out.println("First five cubes: " + Arrays.toString(Arrays.copyOfRange(cubes, 0, 5))); // [0, 1, 8, 27, 64]

        // 对于对象数组,可以使用更复杂的生成逻辑
        String[] complexObjects = new String[10];
        Arrays.parallelSetAll(complexObjects, i -> "Object-" + i);
        System.out.println("Complex objects: " + Arrays.toString(complexObjects));
    }

spliterator

spliterator是 Java 8 中引入的一个方法,用于获取数组的可拆分迭代器(Spliterator)。Spliterator 是一个用于遍历和分割数据源的接口,它可以用于并行处理数据。Arrays类中的spliterator方法提供了一种机制,使得可以将数组元素以一种能够支持并行处理的方式进行迭代。

  • 该方法返回一个Spliterator接口的实现对象,这个对象可以用于遍历数组元素。它支持多种操作,如tryAdvance(逐个元素尝试前进并执行操作)、forEachRemaining(对剩余元素执行操作)等。

  • 更重要的是,Spliterator 可以被分割,这对于并行处理非常关键。例如,在并行流处理中,可以将数据源分割成多个部分,每个部分可以由不同的线程处理,从而提高处理效率。它有一个trySplit方法,用于尝试将 Spliterator 分割成两个部分。如果分割成功,返回一个新的 Spliterator 代表分割后的一部分,原 Spliterator 则代表另一部分;如果无法分割(如数组元素太少等情况),则返回null

示例代码

 public static void spliteratorExample() {
        // 创建一个整数数组
        int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

        // 使用 spliterator() 方法创建一个 Spliterator 对象
        java.util.Spliterator<Integer> spliterator = Arrays.spliterator(numbers);

        // 使用 tryAdvance() 方法遍历 Spliterator
        System.out.println("Traversing the Spliterator:");
        spliterator.tryAdvance(System.out::println);

        // 使用 forEachRemaining() 方法遍历剩余元素
        spliterator.forEachRemaining(System.out::println);

        // 使用 trySplit() 方法拆分 Spliterator
        java.util.Spliterator<Integer> split = spliterator.trySplit();

        // 使用 tryAdvance() 方法遍历拆分后的 Spliterator
        System.out.println("Traversing the split Spliterator:");
        if (split != null) {
            split.tryAdvance(System.out::println);
            split.forEachRemaining(System.out::println);
        }

        // 使用 estimateSize() 方法获取剩余元素数量
        System.out.println("Remaining elements in the original Spliterator: " + spliterator.estimateSize());
        // 使用 characteristics() 方法获取 Spliterator 的特性
        System.out.println("Characteristics of the original Spliterator: " + spliterator.characteristics());
        // 使用 getExactSizeIfKnown() 方法获取确切元素数量
        System.out.println("Exact size of the original Spliterator: " + spliterator.getExactSizeIfKnown());
        // 使用 hasCharacteristics() 方法检查 Spliterator 的特性
        System.out.println("The original Spliterator is SIZED: " + spliterator.hasCharacteristics(java.util.Spliterator.SIZED));
    }

stream

stream方法是 Java 8 中非常重要的一个特性相关的方法,它允许将数组转换为流(Stream)。流是一种用于处理元素序列的高级抽象,支持一系列的中间操作(如filtermap等)和终端操作(如forEachreduce等),可以让开发者以一种更函数式的方式来处理数据。

示例代码

public static void streamTest(){
        String[] stringArray = {"apple", "banana", "cherry", "date"};
        Stream<String> stream = Arrays.stream(stringArray);
        stream.filter(str -> str.length() > 5)
                .map(String::toUpperCase)
                .forEach(System.out::println);
    }

在这个示例中,首先将String数组stringArray转换为Stream<String>,然后使用filter操作过滤出长度大于5的字符串(这里只有bananacherry),接着使用map操作将过滤后的字符串转换为大写形式,最后使用forEach操作将结果打印出来,输出为BANANACHERRY

JDK 9 新增方法

compare

``Arrays.compare()方法用于比较两个数组。它会根据数组中元素的顺序,从第一个元素开始逐个比较,直到找到不同的元素或者遍历完整个数组。这个方法返回一个整数值来表示两个数组的大小关系,返回值的规则遵循Comparator接口的一般约定。

compareUnsigned

Arrays.compareUnsigned()方法主要用于无符号比较两个数组,特别是在处理无符号数据类型(如byteshortintlong)时非常有用。它和compare方法类似,但在比较过程中会将数据视为无符号数。

示例代码

  public static void compareTest(){
        int[] array1 = {1, 2, 3};
        int[] array2 = {1, 2, 4};
        int result = Arrays.compare(array1, array2);
        System.out.println("比较结果: " + result);

        // 示例 2 compareUnsigned
        int result2 = Arrays.compareUnsigned(array1, array2);
        System.out.println("比较结果(无符号比较): " + result2);

    }

mismatch

mismatch() 方法用于比较两个数组,并找到第一个不匹配(即元素值不同的位置)。如果两个数组在所有对应位置上的元素都相等,则返回 -1;否则,返回第一个不同元素的索引。对于长度不同的数组,mismatch() 会返回较短数组的长度,因为超出部分默认视为不匹配。

mismatch() 方法适用于基本类型数组和对象数组,并且能够处理多维数组的情况。它提供了一种高效的方式来定位两个数组之间的差异点,这对于调试、测试以及数据验证等场景非常有用。

示例代码

 public static void missMatchTest() {
        // 创建两个整型数组进行比较
        int[] array1 = {1, 2, 3, 4, 5};
        int[] array2 = {1, 2, 0, 4, 5};

        // 使用 mismatch 查找第一个不匹配的位置
        int index = Arrays.mismatch(array1, array2);
        System.out.println("First mismatch at index: " + index); // 输出 2,因为第三个元素不同

        // 如果两个数组完全相同
        int[] array3 = {1, 2, 3, 4, 5};
        index = Arrays.mismatch(array1, array3);
        System.out.println("First mismatch at index: " + index); // 输出 -1,表示没有不匹配的地方
    }

参考文献/AIGC

通义tongyi.ai_你的全能AI助手-通义千问

豆包

相关推荐

JavaUsefulMode: 基于Java 语言的自定义实用工具集

JavaUsefulMode是小编编写Java方向学习专栏时的代码示例汇总总结,其中内容包含了该篇文章所涉及的Java代码示例。感兴趣的小伙伴可以直接下载学习。

Wend看源码-Java.util 工具类学习(上)-CSDN博客

Wend看源码-Java.util 工具类学习(下)-CSDN博客

Wend看源码-Java-Collections 工具集学习-CSDN博客

Wend看源码-Java-Executor异步执行器学习-CSDN博客

Wend看源码-Java-fork/Join并行执行任务框架学习-CSDN博客

相关推荐
架构文摘JGWZ5 分钟前
一键完成!!网页打包成桌面应用
开发语言·学习·开源软件·工具
快乐非自愿5 分钟前
一文解秘Rust如何与Java互操作
java·开发语言·rust
SomeB1oody7 分钟前
【Rust自学】10.8. 生命周期 Pt.4:方法定义中的生命周期标注与静态生命周期
开发语言·后端·rust
小万编程8 分钟前
基于SpringBoot+Vue毕业设计选题管理系统(高质量源码,提供文档,免费部署到本地)
java·vue.js·spring boot·计算机毕业设计·java毕业设计·web毕业设计
m0_7482350711 分钟前
使用rustDesk搭建私有远程桌面
java
Source.Liu12 分钟前
【学Rust开发CAD】1 环境搭建
开发语言·rust
快乐是19 分钟前
发票打印更方便
java
文浩(楠搏万)22 分钟前
Java内存管理:不可达对象分析与内存泄漏优化技巧 Eclipse Memory Analyzer
java·开发语言·缓存·eclipse·内存泄漏·不可达对象·对象分析
圆蛤镇程序猿24 分钟前
【什么是MVCC?】
java·数据库·oracle
m0_7482567826 分钟前
【SQL】掌握SQL查询技巧:数据分组与排序
java·jvm·sql