Java中如何反转int数组?

295

我正在尝试在Java中反转一个int数组。

该方法无法反转数组。

for(int i = 0; i < validData.length; i++)
{
    int temp = validData[i];
    validData[i] = validData[validData.length - i - 1];
    validData[validData.length - i - 1] = temp;
}

它有什么问题?


39
我明白我哪里做错了。应该是validData.length/2才对。否则它会反转然后再次反转回来。 - MichaelScott
4
请参阅 http://en.wikipedia.org/wiki/In-place_algorithm,其中包含该算法的正确版本描述。 - Dean Povey
Java 8: https://dev59.com/KnI95IYBdhLWcg3w1BhN#46756353 - akhil_mittal
48个回答

344

要反转一个int数组,你需要交换项直到达到中点,像这样:

for(int i = 0; i < validData.length / 2; i++)
{
    int temp = validData[i];
    validData[i] = validData[validData.length - i - 1];
    validData[validData.length - i - 1] = temp;
}
你目前的做法是每个元素都交换两次,因此结果和初始列表相同。

1
我想把validData.length / 2部分放到for循环的外面。 - Jin Kwon
11
@Jin,我不会这么做。这只会使意思更加晦涩,而且我敢打赌优化编译器会为你完成它。无论如何,在从分析中清楚地证明需要或有帮助之前,微观优化是毫无意义的。 - Nicu Stiurca
9
那有点像执行validData.length >> 1。这等效且更快,但会让很多程序员感到困惑,而任何一个好的编译器都会自动执行该操作。 - Justin
2
你应该只计算一次 validData.length - i - 1 并将其保存到一个变量中。 - user2258887
2
@sg28 消除临时变量的意义是什么? - Stefan Reich
显示剩余2条评论

330

使用Commons.Lang,你可以简单地使用

ArrayUtils.reverse(int[] array)

大多数情况下,使用已经过单元测试和用户测试的易于获取的库来解决问题可以更快速且更不容易出错。


6
我更希望它返回反转(传递)的数组,以便于函数式编程风格。 - Laurent G
2
@laurent-g 说句公道话:用这种方式反转数组更节省内存,这可能就是他们这样做的原因。 - Sirmyself
9
我的意思不是复制或不复制。我的消息中指出需要返回“(passed)”(在被翻转后),因此它可以在表达式中传递,而不需要单独的语句。 - Laurent G
1
如果我不想使用ArrayUtils怎么办?你如何手动实现它? - Edison Pebojot
@LaurentG 可以编写一个辅助函数 private int[] reversed(int[] array) {ArrayUtils.reverse(array);},然后你就可以这样使用 f(reversed(arr)); :)) - Coder
显示剩余2条评论

84
Collections.reverse(Arrays.asList(yourArray));

java.util.Collections.reverse() 可以反转 java.util.Listjava.util.Arrays.asList() 返回一个包装了传递给它的特定数组的列表,因此在调用 Collections.reverse() 后,yourArray 就被反转了。

这种方法只需要创建一个 List 对象,不需要添加额外的库。

Tarik 和他们的评论员已经提出了类似的解决方案,但我认为这个答案更简洁、更易于理解。


38
对于对象数组,这是一个很好的解决方案。但是对于基本类型数组则不适用。例如,将int[]传递给asList(...)将不会返回一个List<Integer>,而是一个包含一个元素的List<int[]>。目前似乎没有简单内置的方法将int[]转换为Integer[] - Martin Rust
10
这不适用于基本数组...集合不返回值,因此现在你在内存中有一个无用的数组作为列表。 - NightSkyCode
1
@MartinRust Java 8+:Arrays.stream(arr).boxed().collect(Collectors.toList())Arrays.stream(arr).boxed().toArray(Integer[]::new) - Simon Forsberg
1
@KingLogic 嗯,这是一行代码,也是我能想到的最漂亮的解决方案。如果你有更好的建议(不依赖于任何库),请随意提出。 - Simon Forsberg

61
public class ArrayHandle {
    public static Object[] reverse(Object[] arr) {
        List<Object> list = Arrays.asList(arr);
        Collections.reverse(list);
        return list.toArray();
    }
}

11
当然会。一个列表只能保存对象,而不能保存原始数据类型,所以所有的原始数据类型(在这种情况下是int)都被包装成它们相应的包装类(在这种情况下是Integer),并放入列表中。你知道,Integer是对象。@Tom - 11684
8
注意:如果我没记错的话,原始数组已经被修改了。 为了清楚起见,你可能不想返回任何东西。 - Andrea Zilio
3
是的,通用列表只能容纳对象。但该方法需要一个数组。数组可以容纳原始数据类型。因此,int[] 不同于 Integer[]。尝试一下:Integer[] array = new int[5]。你会得到一个编译错误。这就是为什么 Java 的 Arrays 类定义了一堆用于处理原始数组的方法。试图将 int[] 传递给上述方法将导致类似于 The method reverse(Object[]) in the type MakeSimple is not applicable for the arguments (int[]) 的东西。对于原地算法,使用的内存较少且运行速度更快。 - Brian McCutchon
3
实际上,Arrays.asList() 返回的列表不引用原始数组,返回的数组也是如此。这个方法的一个问题就是它使用三倍的内存,并且需要三倍的工作来完成一个原地算法的任务。 - Brian McCutchon
4
这种方法本身可能有效,但是无法将 int[] 作为参数传递给这个方法(*"incompatible types: int[] cannot be converted to Object[]"*)。 - MC Emperor
显示剩余4条评论

45

我认为,如果您在循环的每次迭代中声明显式变量来跟踪要交换的索引,那么就更容易理解算法的逻辑。

public static void reverse(int[] data) {
    for (int left = 0, right = data.length - 1; left < right; left++, right--) {
        // swap the values at the left and right indices
        int temp = data[left];
        data[left]  = data[right];
        data[right] = temp;
    }
}

我也认为用while循环来完成这个操作更易读。

public static void reverse(int[] data) {
    int left = 0;
    int right = data.length - 1;

    while( left < right ) {
        // swap the values at the left and right indices
        int temp = data[left];
        data[left] = data[right];
        data[right] = temp;

        // move the left and right index pointers in toward the center
        left++;
        right--;
    }
}

传统的交换看起来更容易,但是当涉及到数组索引值 left、right 等时,如果有任何调试问题,它们会很有帮助。 - Srinath Ganesh
你也可以添加 'public static void swap(int[] data, int index1, int index2) { ... }' 并在 'reverse' 中像这样使用它:swap(data, left, right)。 - pm_

17

使用流来翻转

这里已经有很多答案了,主要集中在原地修改数组。但是为了完整性,这里提供另一种方法,使用Java流来保留原始数组并创建一个新的翻转数组:

int[] a = {8, 6, 7, 5, 3, 0, 9};
int[] b = IntStream.rangeClosed(1, a.length).map(i -> a[a.length-i]).toArray();

14

Guava

使用Google Guava库:

Collections.reverse(Ints.asList(array));

5
太棒了!简短而有效。像所有的“asList”方法一样,它创建了一个 视图,直接写入支持(原始)数组。我认为这里的踩负者错误地认为这返回了一个装箱的列表或其他东西。 - Luke Usherwood
确实,这值得注意。我认为在我个人处理的大部分代码中,这不会是一个大问题——我们的“热点”区域已经很好地定义了,其余部分是某种形式的“胶水代码”。同时,我也意识到内存波动还会产生额外的“隐藏”成本,而性能分析器并没有将其归因于实际函数。 - Luke Usherwood
2
@AnthonyJClink 不确定 "it" 指的是什么,但 JDK 实用程序 Collections.reverse 是一个 void 方法。它在一个 Guava 内部类上原地操作,该内部类包装了一个 int[](由于它从不存储装箱的 Integer 列表,因此我不会称该类为 "boxed list",而是称其为 "数组的列表视图")。但是,是的,它通过传递 Integer 对象来操作接口,因此这将创建大量临时对象和装箱,如上所述。在需要性能的地方,请尝试使用 IntStream 或基本集合库(Trove、Koloboke、Eclipse Collections 等)。 - Luke Usherwood
根据Ints的文档:方法 修饰符和类型 方法和描述 static List<Integer> asList(int... backingArray) 返回由指定数组支持的固定大小列表,类似于Arrays.asList(Object[])。因此...它是一个装箱列表。 - AnthonyJClink
@AnthonyJClink 好的,听起来我们对它的工作原理达成了一致。 - Luke Usherwood
显示剩余5条评论

13

如果使用 Java 8,我们可以使用IntStream来反转整数数组,如下所示:

int[] sample = new int[]{1,2,3,4,5};
int size = sample.length;
int[] reverseSample = IntStream.range(0,size).map(i -> sample[size-i-1])
                      .toArray(); //Output: [5, 4, 3, 2, 1]

10
for(int i=validData.length-1; i>=0; i--){
  System.out.println(validData[i]);
 }

1
很遗憾,这是目前最干净的答案,因为每个开发人员都知道如何做,并且不需要安装任何扩展包。 - HoldOffHunger
3
这对于获取数组的值很有帮助,但如果你真的想要反转数组,你将不得不使用这种方法创建一个新的数组 => 其他的方法会更有效率。 - Cactusroot

9

简单的for循环!

for (int start = 0, end = array.length - 1; start <= end; start++, end--) {
    int aux = array[start];
    array[start]=array[end];
    array[end]=aux;
}

4
将来,请明确告知提问者他们做错了什么,以及您做对了什么。 - Kartik Chugh
1
将“start <= end”更改为“start < end”。 - Leonard Pauli

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接