Arrays工具类教你优雅地管理数组数据

news/2025/3/17 8:15:30/文章来源:https://www.cnblogs.com/seven97-top/p/18774785

数组专用工具类指的是 java.util.Arrays 类,基本上常见的数组操作,这个类都提供了静态方法可供直接调用。毕竟数组本身想完成这些操作还是挺麻烦的,有了这层封装,就方便多了。

package java.util;
/*** @author Josh Bloch* @author Neal Gafter* @author John Rose* @since  1.2*/
public class Arrays {}

方法一览

方法名 简要描述
asList() 返回由指定数组支持的固定大小的列表。
sort() 将数组排序(升序)
parallelSort() 将指定的数组按升序排序
binarySearch() 使用二分搜索法快速查找指定的值(前提是数组必须是有序的)
compare() 按字典顺序比较两个数组
compareUnsigned() 按字典顺序比较两个数组,将数字元素处理为无符号
copyOf() 填充复制数组
copyOfRange() 将数组的指定范围复制到新数组
fill() 将指定元素填充给数组每一个元素
equals() 比较两个数组
deepEquals() 比较两个数组深度
toString() 将数组转换为字符串
deepToString() 将一个多维数组转换为字符串
mismatch() 查找并返回两个数组之间第一个不匹配的索引,如果未找到则返回-1
parallelPrefix() 使用提供的函数对数组元素进行操作
parallelSetAll() 使用提供的生成器函数并行设置指定数组的所有元素以计算每个元素
setAll() 使用提供的生成器函数设置指定数组的所有元素以计算每个元素

asList()

  • 功能:返回由指定数组支持的固定大小的列表
  • 参数:asList​(T… a)
  • 返回值:一个列表
List < String > ss = Arrays.asList("hello", "world");
// List<String> ss1 = Arrays.asList("hello", "world",1);   报错,类型必须一致(泛型)
System.out.println(ss); //[hello, world]//  ss.add("java");  //UnsupportedOperationException  会报错
//  ss.remove(1);   //UnsupportedOperationException  会报错System.out.println(ss.get(0)); //hello
ss.set(0, "java");
System.out.println(ss); //[java, world]

需要注意的是,add方法和remove会报错。

这是因为asList() 返回的是Arrays类的内部类:

public static < T > List < T > asList(T...a) {return new ArrayList < > (a);
}

这个内部类也继承了 java.util.AbstractList 类,重写了很多方法,比如contains方法、set方法,但是却没有重写add方法,最终是调用了父类的add(int, E)方法,所以在调用add方法时才会抛出java.lang.UnsupportedOperationException异常。

关于这一点,在《阿里巴巴Java开发手册》中,也有提及:使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。

所以大家在使用Arrays.asList时还是要注意下,避免踩坑。

toString() 和 deepToString()

  • 功能:将数组转换为字符串
  • 参数:待转化数组
  • 返回值:转化后的字符串

代码示例:

public void toStringTest() {String[] str = {"java", "hello", "javascript"};String[][] strs = {{"a", "b"}, {"c"}, {"d", "e"}};System.out.println(Arrays.toString(str));//[java, hello, javascript]System.out.println(Arrays.toString(strs));//[[Ljava.lang.String;@4563e9ab, [Ljava.lang.String;@11531931, [Ljava.lang.String;@5e025e70]//普通的toString()方法只转化一层,内层还是地址值System.out.println(Arrays.deepToString(strs));//可以深度转换//[[a, b], [c], [d, e]]
}

方法源码:

public static String toString(Object[] a) {// 先判断 null,是的话,直接返回“null”字符串;if (a == null)return "null";//获取数组的长度int iMax = a.length - 1;//如果数组的长度为 0( 等价于 length - 1 为 -1),返回中括号“[]”,表示数组为空的;if (iMax == -1)return "[]";//如果数组既不是 null,长度也不为 0,就声明 StringBuilder 对象,StringBuilder b = new StringBuilder();//添加一个数组的开始标记“[”b.append('[');//遍历数组,把每个元素添加进去for (int i = 0; ; i++) {b.append(String.valueOf(a[i]));//当遇到末尾元素的时候(i == iMax),不再添加逗号和空格“, ”,而是添加数组的闭合标记“]”。if (i == iMax)return b.append(']').toString();b.append(", ");}
}

sort() 和 parallelSort()

  • 功能:都是将数组排序(默认升序,支持lambda,泛型),默认的排序算法是 Dual-Pivot Quicksort
  • 参数:
    sort(Object[] a[, int fromIndex, int toIndex]) 或者 sort(T[] a[, int fromIndex, int toIndex,] Comparator<? super T> c)
    parallelSort(Object[] a[, int fromIndex, int toIndex]) 或者 parallelSort(T[] a[, int fromIndex, int toIndex,] Comparator<? super T> c)

代码示例:

public void sortTest() {String[] str = {"abc", "add", "java", "hello", "javascript"};int[] ii = {1, 8, 99, 222, 35};//单参数情况Arrays.sort(str);  //默认全排,字母会按照字母表顺序Arrays.sort(ii);System.out.println(Arrays.toString(str));  //[abc, add, hello, java, javascript]System.out.println(Arrays.toString(ii));  //[1, 8, 35, 99, 222]//多参数情况Arrays.sort(str,2,4);   //只排列指定范围内的Arrays.sort(ii,2,4);System.out.println(Arrays.toString(str));  //[abc, add, hello, java, javascript]System.out.println(Arrays.toString(ii));  //[1, 8, 99, 222, 35]//可传入lambda表达式,指定排序比较器(多参数情况可指定开始结束位置)Arrays.sort(str, (a, b) -> b.compareTo(a));  //降序System.out.println(Arrays.toString(str));  //[javascript, java, hello, add, abc]//parallelSort()方法目前我实验感觉与sort()相差无几,基本相似Arrays.parallelSort(str);System.out.println(Arrays.toString(str)); //[abc, add, hello, java, javascript]Arrays.parallelSort(str, (a, b) - > b.compareTo(a));System.out.println(Arrays.toString(str)); //[javascript, java, hello, add, abc]
}

sort源码

public static void sort(int[] a) { //整体排序DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}public static void sort(int[] a, int fromIndex, int toIndex) { //部分排序rangeCheck(a.length, fromIndex, toIndex);DualPivotQuicksort.sort(a, fromIndex, toIndex - 1, null, 0, 0);
}

从源码上看整体排序并不是调用的部分排序的方法,Arrays.sort(int[] a)和Arrays.sort(int[] a, int fromIndex, int toIndex)只是个入口,它们都会去调用DualPivotQuicksort.sort方法,都会传入排序部分的起终点,不过整体排序传入的起终点为0和length - 1。

从数组元素的类型来看,可以将Arrays.sort分为对基本数据类型的排序和对泛型及Object数组的排序。进入源码我们可以发现:对于基本数据类型数组的排序,Arrays.sort都将调用DualPivotQuicksort.sort方法,而泛型及Object数组的排序实现则与之不同。

对基本数据类型数组的排序

对于基本数据类型数组的排序,Arrays.sort都将调用DualPivotQuicksort.sort方法,来看看这个方法的部分源码(感兴趣可自行阅读,或可直接往后看结论):

static void sort(int[] a, int left, int right,int[] work, int workBase, int workLen) {// 若数组长度<286,将调用sort(a, left, right, true)if (right - left < QUICKSORT_THRESHOLD) {sort(a, left, right, true);return;}/** Index run[i] is the start of i-th run* (ascending or descending sequence).*/int[] run = new int[MAX_RUN_COUNT + 1];int count = 0;run[0] = left;// Check if the array is nearly sortedfor (int k = left; k < right; run[count] = k) {if (a[k] < a[k + 1]) { // ascendingwhile (++k <= right && a[k - 1] <= a[k]);} else if (a[k] > a[k + 1]) { // descendingwhile (++k <= right && a[k - 1] >= a[k]);for (int lo = run[count] - 1, hi = k; ++lo < --hi;) {int t = a[lo];a[lo] = a[hi];a[hi] = t;}} else { // equalfor (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k];) {if (--m == 0) {sort(a, left, right, true);return;}}}/** The array is not highly structured,* use Quicksort instead of merge sort.*/if (++count == MAX_RUN_COUNT) {sort(a, left, right, true);return;}}// Check special cases// Implementation note: variable "right" is increased by 1.if (run[count] == right++) { // The last run contains one elementrun[++count] = right;} else if (count == 1) { // The array is already sortedreturn;}// Determine alternation base for mergebyte odd = 0;for (int n = 1;(n <<= 1) < count; odd ^= 1);// Use or create temporary array b for mergingint[] b; // temp array; alternates with aint ao, bo; // array offsets from 'left'int blen = right - left; // space needed for bif (work == null || workLen < blen || workBase + blen > work.length) {work = new int[blen];workBase = 0;}if (odd == 0) {System.arraycopy(a, left, work, workBase, blen);b = a;bo = 0;a = work;ao = workBase - left;} else {b = work;ao = 0;bo = workBase - left;}// Mergingfor (int last; count > 1; count = last) {for (int k = (last = 0) + 2; k <= count; k += 2) {int hi = run[k], mi = run[k - 1];for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {b[i + bo] = a[p++ +ao];} else {b[i + bo] = a[q++ +ao];}}run[++last] = hi;}if ((count & 1) != 0) {for (int i = right, lo = run[count - 1]; --i >= lo; b[i + bo] = a[i + ao]);run[++last] = right;}int[] t = a;a = b;b = t;int o = ao;ao = bo;bo = o;}
}

若数组长度<286,调用sort(a, left, right, true)

若数组长度<286,调用sort(a, left, right, true)

也就是说,若数组长度小于286,则会再次判断:

  • 若数组长度较小,,长度<47,则使用插入排序
  • 若数组长度>=47,将使用快速排序

这是由排序算法的特性决定的,因为在数组长度很小时,在大量测试的平均结果下,插入排序将快于快排。

那么当数组长度>=286时呢?重新回到DualPivotQuicksort.sort方法,发现会对数组的结构性进行判断:

  • 若数组基本有序,则将使用归并排序
  • 若数组的元素排列较为混乱,则调用sort(a, left, right, true)方法,由于数组长度>=286,也>=47,因此会进行快速排序。

为什么这样设计也是由排序算法的特性决定的,虽然快排和归并排序的(平均)时间复杂度是一样的,但对于基本有序的数组,归并排序的速度会比快速排序快,而对于近乎无序的数组,归并排序速度会比快速排序慢。

总结一下,对于基本数据类型数组的排序,排序算法的选择和数组长度的关系如下:

数组长度 所使用的排序算法
length < 47 插入排序
47 <= length < 286 快速排序
length >= 286 且数组基本有序 归并排序
length >= 286 且数组基本无序 快速排序

对Object数组和泛型数组的排序

对于泛型数组的排序,可以传入实现了Comparator接口的类的对象,也可以不传,实际上传和不传都是调用的同一个方法,只不过不传入时,对应的参数为null。我们来看看Arrays.sort对Object数组和泛型数组的排序源码:

public static void sort(Object[] a) {// jdk1.7之前的排序用的就是归并排序,legacyMergeSort此方法就是1.7为了兼容之前版本的归并排序。if (LegacyMergeSort.userRequested)legacyMergeSort(a);elseComparableTimSort.sort(a, 0, a.length, null, 0, 0);
}

JDK8会默认选择TimSort作为排序算法。TimSort算法是一种起源于归并排序和插入排序的混合排序算法,原则上TimSort是归并排序,但小片段的合并中用了插入排序。对于泛型数组的排序,若不传入实现了Comparator接口的类的对象,将调用sort(Object[] a)方法

接下来看调用的ComparableTimSort.sort方法的部分源码:

ComparableTimSort.sort 会调用countRunAndMakeAscending方法和binarySort方法,而这两个方法都有将数组元素强转为Comparable接口类型的操作,因为它需要调用Comparable接口中的compareTo方法进行元素间的比较,Comparable接口中只定义了一个方法,那就是compareTo。

 private static int countRunAndMakeAscending(Object[] a, int lo, int hi) {assert lo < hi;int runHi = lo + 1;if (runHi == hi)return 1;if (((Comparable) a[runHi++]).compareTo(a[lo]) < 0) { // Descending//将数组元素强转为Comparable接口类型while (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) < 0)runHi++;reverseRange(a, lo, runHi);} else { // Ascendingwhile (runHi < hi && ((Comparable) a[runHi]).compareTo(a[runHi - 1]) >= 0)runHi++;}return runHi - lo;}private static void binarySort(Object[] a, int lo, int hi, int start) {assert lo <= start && start <= hi;if (start == lo)start++;for (; start < hi; start++) {//将数组元素强转为Comparable接口类型Comparable pivot = (Comparable) a[start];// Set left (and right) to the index where a[start] (pivot) belongsint left = lo;int right = start;assert left <= right;while (left < right) {int mid = (left + right) >>> 1;//compareTo方法if (pivot.compareTo(a[mid]) < 0)right = mid;elseleft = mid + 1;}assert left == right;int n = start - left; // The number of elements to move// Switch is just an optimization for arraycopy in default caseswitch (n) {case 2:a[left + 2] = a[left + 1];case 1:a[left + 1] = a[left];break;default:System.arraycopy(a, left, a, left + 1, n);}a[left] = pivot;}
}

因此,若调用Arrays.sort(Object[] o)对Object数组进行排序,但数组元素类型表示的类并没有实现Comparable接口,那么Java将认为该类的对象是无法比较的,那么就会抛出ClassCastException异常.

小结

JDK中的Arrays.sort实际上采用的是设计模式中的模板模式,将排序算法的步骤封装了起来,而将如何比较两个数组元素交给了程序员来实现。
当排序自定义类时,可以让这个类实现Comparable接口,并重写其compareTo方法。也可以创建一个实现了Comparator接口的类,重写其compare方法。具体如何比较两个数组元素的逻辑就写在了需要重写的这两个方法中。

比较两个数组元素o1与o2的大小无非三种结果:o1>o2,o1=o2,o1<o2。因此compareTo方法和compare方法的返回值有三种情况,这是针对默认升序设计的:

  • 当o1 > o2,返回一个正整数;
  • 若o1 = o2,返回0;
  • 若o1 < o2,返回一个负整数。

对于实现了Comparable接口的类,o1 即为this,表示当前类对象。若在重写方法的逻辑中按上述对应关系去返回对应值(即return o1 -o2),则调用Arrays.sort将会得到升序结果;若把对应关系写反((即return o2 -o1)),则会得到降序结果。

parallelSort()

parallelSort() 在功能上有所不同。与 sort() 使用单个线程对数据进行顺序排序不同,它使用 并行排序-合并排序 算法。它将数组分成子数组,这些子数组本身先进行排序然后合并。为了执行并行任务,它使用 ForkJoin 池。

parallelSort源码:如果数组大小小于或等于 8192,或者处理器只有一个核心,则它将使用顺序的 Dual-Pivot Quicksort 算法。否则,它使用并行排序。

public static < T extends Comparable <? super T >> void parallelSort(T[] a) {int n = a.length, p, g;// MIN_ARRAY_SORT_GRAN = 1<<13,即8192if (n <= MIN_ARRAY_SORT_GRAN ||//或者处理器只有一个核心(p = ForkJoinPool.getCommonPoolParallelism()) == 1)//使用顺序的 Dual-Pivot Quicksort 算法DualPivotQuicksort.sort(a, 0, n - 1, null, 0, 0);elsenew ArraysParallelSortHelpers.FJInt.Sorter(null, a, new int[n], 0, n, 0,((g = n / (p << 2)) <= MIN_ARRAY_SORT_GRAN) ?MIN_ARRAY_SORT_GRAN : g).invoke();
}

小结

当要排序的数据集很大时,parallelSort() 可能是更好的选择。但是,在数组较小的情况下,最好使用 sort(),因为它可以提供更好的性能。

binarySearch()

  • 功能:使用二分搜索法快速查找指定的值(前提是数组必须是有序的,支持lambda表达式,泛型)
  • 参数:binarySearch(Object[] a[, int fromIndex, int toIndex], Object key)
  • 返回值:有则返回对应下标,无则返回负值

代码示例:

public void binarySearchTest() {int[] a = {1, 5, 7, 4, 6, 7, 8, 4, 9, 0};Arrays.sort(a); //必须先排序System.out.println(Arrays.toString(a));//[0, 1, 4, 4, 5, 6, 7, 7, 8, 9]System.out.println(Arrays.binarySearch(a, 4)); //2System.out.println(Arrays.binarySearch(a, 11)); //-11//也可指定范围查找,其查找机制是折半查找,每次缩小一半范围
}

源码:

private static int binarySearch0(int[] a, int fromIndex, int toIndex, int key) {int low = fromIndex;int high = toIndex - 1;while (low <= high) {int mid = (low + high) >>> 1;int midVal = a[mid];if (midVal < key)low = mid + 1;else if (midVal > key)high = mid - 1;elsereturn mid; // key found}return -(low + 1); // key not found.
}

创建数组

使用 Arrays 类创建数组可以通过以下三个方法:

  • copyOf,复制指定的数组,截取或用 null 填充
  • copyOfRange,复制指定范围内的数组到一个新的数组
  • fill,对数组进行填充

copyOf 和copyOfRange

  • 功能:复制填充数组
  • 参数
    copyOf(int[] original, int newLength)
    copyOf(T[] original, int newLength)
    copyOfRange(int[] original, int from, int to)
    copyOfRange(T[] original, int from, int to)
    copyOfRange(U[] original, int from, int to, class <? extends T[]> newType)
  • 返回值:复制填充后的数组
  • 区别
    copyOf()是从原数组0位置开始拷贝指定长度到新数组;
    copyOfRange()是从原数组中指定范围拷贝到新数组,如果指定长度或者范围超出原数组范围,则超出部分会补上此数据类型的默认值,如String类型会补null,int型会补0

代码示例

public void copyOfTest() {int[] arr = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};int[] arr1 = Arrays.copyOf(arr, 5); //拷贝长度为5,第二个参数为新数组的长度int[] arr2 = Arrays.copyOf(arr, 15);System.out.println(Arrays.toString(arr1)); //[1, 2, 3, 4, 5]System.out.println(Arrays.toString(arr2)); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0]arr[0] = 20; //改变原数组System.out.println(Arrays.toString(arr)); //原数组改变System.out.println(Arrays.toString(arr1)); //复制后的数组不变String[] str = {"java", "hello", "world"};String[] str1 = Arrays.copyOf(str, 2);String[] str2 = Arrays.copyOf(str, 5);System.out.println(Arrays.toString(str1)); //[java, hello]System.out.println(Arrays.toString(str2)); //[java, hello, world, null, null]//copyOfRange()int[] arrs = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};int[] arr3 = Arrays.copyOfRange(arrs, 2, 8); //[3, 4, 5, 6, 7, 8]int[] arr4 = Arrays.copyOfRange(arrs, 5, 15); //[6, 7, 8, 9, 0, 0, 0, 0, 0, 0]System.out.println(Arrays.toString(arr3));System.out.println(Arrays.toString(arr4));arrs[6] = 99; //改变原数组System.out.println(Arrays.toString(arrs)); //[1, 2, 3, 4, 5, 6, 99, 8, 9, 0]  原数组改变System.out.println(Arrays.toString(arr3)); //[3, 4, 5, 6, 7, 8]  复制后的不会随着改变
}

fill

  • 功能:将指定元素填充给数组每一个元素
  • 参数:fill​(int[] a, 【int fromIndex, int toIndex】, int val)
  • 返回值:无

代码示例:

String[] stutter = new String[4];
Arrays.fill(stutter, "seven");
System.out.println(Arrays.toString(stutter));//[seven, seven, seven, seven]

源码如下:

public static void fill(Object[] a, int fromIndex, int toIndex, Object val) {rangeCheck(a.length, fromIndex, toIndex);//遍历填充for (int i = fromIndex; i < toIndex; i++)a[i] = val;
}

setAll 和 parallelSetAll()

Java 8 新增了 setAll() 方法,它提供了一个函数式编程的入口,可以对数组的元素进行填充:

int[] array = new int[10];
// i 就相当于是数组的下标,值从 0 开始,到 9 结束,那么 `i * 10` 就意味着值从 0 * 10 开始,到 9 * 10 结束
Arrays.setAll(array, i -> i * 10);
System.out.println(Arrays.toString(array));//[0, 10, 20, 30, 40, 50, 60, 70, 80, 90]

可以用来为新数组填充基于原来数组的新元素。

parallelSetAll() 就是 setAll 的并行版本,都是通过索引值去改变元素,改编后的值与索引有关

String[] str1 = {"a", "b", "c"};
Arrays.parallelSetAll(str1, (m) -> m + "haha");
System.out.println(Arrays.toString(str1));  //[0haha, 1haha, 2haha]

equals() 和 deepEquals()

Arrays 类的 equals() 方法用来判断两个数组是否相等,来看下面这个例子:

String[] s1 = new String[] { "s", "e", "v", "e" };
System.out.println(Arrays.equals(new String[]{"s", "e", "v", "e"}, s1));//true
System.out.println(Arrays.equals(new String[]{"s", "e", "v", "n"}, s1));//falseString[][] s4 = {{"hello"}, {"java"}, {"c++"}, {"python"}};
String[][] s5 = {{"hello"}, {"java"}, {"c++"}, {"python"}};
System.out.println(Arrays.deepEquals(s4, s5));  //true
System.out.println(Arrays.equals(s4, s5));    //falseint[][] a = {{1,2},{3,4}};
int[][] b =  {{1,2},{3,4}};
System.out.println(Arrays.deepEquals(a,b));// true

区别在于:

  • equals默认从头比较到尾,也可以指定范围,但是deepEquals不能指定范围
  • deepEquals可以比较一维数组,也支持比较多维数组,而equals不能
  • 当deepEquals比较一维数组时,不支持比较基本类型数组,如int[],但支持int[][]

equals() 方法的源码:

public static boolean equals(Object[] a, Object[] a2) {//数组是一个对象,所以先使用“==”操作符进行判断if (a==a2)return true;//再判断是否为 null,其中一个为 null,返回 falseif (a==null || a2==null)return false;int length = a.length;//判断 length,不等的话,返回 falseif (a2.length != length)return false;//否则的话,依次调用 Objects.equals() 比较相同位置上的元素是否相等for (int i=0; i<length; i++) {if (!Objects.equals(a[i], a2[i]))return false;}return true;
}

这里数组是一个对象的问题 可以看这篇文章 数组是不是对象,int[]数组是Object,但不是Object[],deepEquals支持的是Object[],int[][]则属于Object[]

deepEquals() 方法的源码:

public static boolean deepEquals(Object[] a1, Object[] a2) {//同equalsif (a1 == a2)return true;if (a1 == null || a2 == null)return false;int length = a1.length;if (a2.length != length)return false;for (int i = 0; i < length; i++) {Object e1 = a1[i];Object e2 = a2[i];if (e1 == e2)continue;if (e1 == null)return false;//如果是个多维数组,则递归比较boolean eq = deepEquals0(e1, e2);if (!eq)return false;}return true;
}

数组转流 stream()

Arrays 类的 stream() 方法可以将数组转换成流:

String[] intro = new String[] { "沉", "默", "王", "二" };
System.out.println(Arrays.stream(intro).count());

还可以为 stream() 方法指定起始下标和结束下标:

System.out.println(Arrays.stream(intro, 1, 2).count());

如果下标的范围有误的时候,比如说从 2 到 1 结束,则程序会抛出 ArrayIndexOutOfBoundsException 异常:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: origin(2) > fence(1)at java.base/java.util.Spliterators.checkFromToBounds(Spliterators.java:387)

parallelPrefix

parallelPrefix 通过遍历数组中的元素,将当前下标位置上的元素与它之前下标的元素进行操作,然后将操作后的结果覆盖当前下标位置上的元素。

int[] arr = new int[] { 1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> left + right);
System.out.println(Arrays.toString(arr));

上面代码中有一个 Lambda 表达式((left, right) -> left + right),是什么意思呢?上面这段代码等同于:

int[] arr = new int[]{1, 2, 3, 4};
Arrays.parallelPrefix(arr, (left, right) -> {System.out.println(left + "," + right);return left + right;
});
System.out.println(Arrays.toString(arr));

来看一下输出结果就明白了:

1,2
3,3
6,4
[1, 3, 6, 10]

也就是说, Lambda 表达式执行了三次:

  • 第一次是 1 和 2 相加,结果是 3,替换下标为 1 的位置
  • 第二次是 3 和 3 相加,结果是 6,也就是第一次的结果和下标为 2 的元素相加的结果
  • 第三次是 6 和 4 相加,结果是 10,也就是第二次的结果和下标为 3 的元素相加的结果

往期推荐

  • 《SpringBoot》EasyExcel实现百万数据的导入导出
  • 《SpringBoot》史上最全SpringBoot相关注解介绍
  • Spring框架IoC核心详解
  • 万字长文带你窥探Spring中所有的扩展点
  • 如何实现一个通用的接口限流、防重、防抖机制
  • 万字长文带你深入Redis底层数据结构
  • volatile关键字最全原理剖析

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/900214.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROCm技术解析概述

3.2 ROCm技术解析 ROCm是第一个用于GPU计算的开源HPC/Hyperscale级平台,也是独立于编程语言的。将UNIX的选择哲学、极简主义和模块化软件开发引入GPU计算。新的ROCm基础允许为应用程序选择甚至开发工具和语言运行时。 1)[n1] ROCm是为规模而构建的;它支持通过RDMA进行服务器…

AMD GPU上对比语言图像预训练(CLIP)模型的交互

AMD GPU上对比语言图像预训练(CLIP)模型的交互 3.1.1 介绍 对比语言图像预训练(CLIP)是一种连接视觉和自然语言的多模态深度学习模型。它是在OpenAI的论文从自然语言监督中学习可转移的视觉模型(2021)中介绍的,并在大量(4亿)图像字幕对的网络抓取数据上进行了对比训练…

推荐书1《AI芯片开发核心技术详解》、2《智能汽车传感器:原理设计应用》、3《TVM编译器原理与实践》、4《LLVM编译器原理与实践》,谢谢

4本书推荐《AI芯片开发核心技术详解》、《智能汽车传感器:原理设计应用》、《TVM编译器原理与实践》、《LLVM编译器原理与实践》由清华大学出版社资深编辑赵佳霓老师策划编辑的新书《AI芯片开发核心技术详解》已经出版,京东、淘宝天猫、当当等网上,相应陆陆续续可以购买。该…

我的世界 GTNH 传送权限单独指定

起因 在 GTNH 2.7.2 上,对于 Journey Map 传送点的支持需要 op 管理员权限,给管理员权限容易刷物件和其他意外,所以需要限制权限同时又能方便移动。 解决方案 使用 ForgeEssentials 来管理权限,从 Forge-essentials-curseforge 获取 1.7.10 版本的插件,放到服务端 mods 目…

洛谷 P3131 [USACO16JAN] Subsequences Summing to Sevens S(前缀和+模运算性质)

前缀和,模运算性质。做题历程拿到手的时候就是考虑前缀和,毕竟要求区间和,如果暴力做就是N3,那么就开始做,写了个O(n2)复杂度代码,交上去80分...迷了,心想这题难道dp啊,懒得想的我直接看题解,发现大佬用了一个很简单的模运算性质就过去了,即(a - b) mod 7 == 0 那么 …

FastAPI性能优化指南:参数解析与惰性加载

扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长 探索数千个预构建的 AI 应用,开启你的下一个伟大创意第一章:参数解析性能原理 1.1 FastAPI请求处理管线 async def app(scope, receive, send):# 1. 请求解析阶段body = await receive()# 2. 参数验证阶段valid…

MYSQL-索引入门

介绍:结构:语法:create index i on tb_emp(name);show index from tb_emp;drop index i on tb_emp;

事物

操作:start transaction;delete from tb_dept where id=2; delete from tb_emp where dept_id===2;commit;rollback ;只要有失败就可以回滚四大特性ACID:总结:

关于编码转换问题

今天我在写攻防世界的“no-strings-attached”这道题的时候,在处理16进制字符串序列转10进制整数的时候出现了问题,这个问题是关于“utf-8”对某些字节值进行特殊处理导致的。具体情况如下: 首先是我提取的两个16进制字符序列然后我对其进行小端序的进制转换操作,操作如下:…

一点点矩阵

矩阵置0//O(m*n) O(m+n) var setZeroes = function(matrix) {let row=[]let col=[]let n=matrix.lengthlet m=matrix[0].lengthfor(let i=0;i<n;i++){for(let j=0;j<m;j++){if(matrix[i][j]==0){row.push(i)col.push(j)}}}for(let i of row){for(let j=0;j<m;j++){mat…

基于DVB-T的COFDM+16QAM+Viterbi编解码图传通信系统matlab仿真,包括载波定时同步,信道估计

1.算法仿真效果 matlab2022a仿真结果如下(完整代码运行后无水印):仿真操作步骤可参考程序配套的操作视频。2.算法涉及理论知识概要基于DVB-T的COFDM+16QAM+Viterbi编解码通信链路是一种常用的数字视频广播系统,用于实现高效的传输和接收。该系统结合了正交频分复用(COFDM)…

Day15_http协议

每日一题 面试题: 请解释以下问题:HTTP/2 的主要改进有哪些?与 HTTP/1.1 相比,它如何解决“队头阻塞”问题? HTTPS 是如何实现数据加密的?详细说明 TLS 握手过程。 HTTP/3 为什么选择基于 UDP 的 QUIC 协议?它解决了哪些传统 TCP 协议的缺陷?解答: 1. HTTP/2 的改进与…