【问题标题】:Efficient swapping of elements of an array in JavaJava中数组元素的高效交换
【发布时间】:2012-11-25 19:34:34
【问题描述】:

我想知道是否有比执行以下操作更有效的方法来交换数组中的两个元素:

String temp = arr[1];
arr[1] = arr[2];
arr[2] = temp;

好吧,这显然还不错,甚至是错误的,但是我需要经常交换,所以我很感兴趣是否有任何 Lib 或其他东西可以提供更有效的方法来做到这一点?

【问题讨论】:

  • 对于字符串?不,不是。如果您需要经常这样做,您还不如滚动一次自己的功能,然后它就不麻烦了
  • 如果你可以使用 List 而不是 Array,你可以使用 Collections.swap(List, i, j)。
  • @Sergio Nakanishi:有趣的一点,也许我可以切换到 Collections,但是我不确定纯 Array 的使用会不会更快?
  • @Robin:我通常更喜欢使用 ArrayList 而不是数组。 ArrayList 在内部使用数组,调用方法来操作数组的开销并不显着。 《Effective Java》这本书有更完整的理由选择 List 而不是数组。
  • @SergioNakanishi 谢谢你的书提示。我刚用谷歌搜索了一下,感觉还不错。

标签: java algorithm swap


【解决方案1】:

不。你可以有一个函数来使它在你使用它的每个地方都更简洁,但最终,所做的工作将是相同的(加上函数调用的开销,直到/除非 HotSpot 将其移入内联 - 以帮助它,制作函数static final)。

【讨论】:

  • 仅作记录,使用static final 而不是仅使用static 或什至不使用有什么好处?
  • @Robin:static 告诉编译器和虚拟机(记住,HotSpot 是虚拟机,它做了很多运行时优化)它不必担心设置 this,它使内联函数更容易。 final 表示它不会在子类中被覆盖。关于 HotSpot 对其中任何一个的关心程度,我很容易弄错,真的,我不知道 HotSpot 的最新魔法。 :-)
  • 我认为你不能覆盖静态方法。
  • @Robin:类中的所有方法都是在类加载的时候加载的,不管是实例还是静态,更多在Section 12.4 of the JLS
  • @Sergio: “我认为你不能覆盖静态方法。” 你可以,除非它是final,但它与覆盖实例方法有很大不同。请注意覆盖static final (ideone.com/NuMyv4) 的此错误:“错误:Derived 中的 foo() 无法覆盖 Base 中的 foo()...覆盖的方法是静态的,最终的”。如果不是final (ideone.com/vnuAk6),该错误就会消失。只有当您通过实例引用调用静态方法时,它才会发挥作用,这是一件很奇怪的事情。调用的方法在编译时根据引用的类型确定:ideone.com/yXDgpa
【解决方案2】:

这应该使它无缝:

public static final <T> void swap (T[] a, int i, int j) {
  T t = a[i];
  a[i] = a[j];
  a[j] = t;
}

public static final <T> void swap (List<T> l, int i, int j) {
  Collections.<T>swap(l, i, j);
}

private void test() {
  String [] a = {"Hello", "Goodbye"};
  swap(a, 0, 1);
  System.out.println("a:"+Arrays.toString(a));
  List<String> l = new ArrayList<String>(Arrays.asList(a));
  swap(l, 0, 1);
  System.out.println("l:"+l);
}

【讨论】:

  • 这很好用,谢谢。请注意,这需要一个对象数组,因此如果您遇到那些奇怪的编译时错误,请确保您使用 Integer[] array 而不是 int[] array
【解决方案3】:

如果您正在交换数字,并且想要一种简洁的方式来编写代码,而无需创建单独的函数或使用令人困惑的 XOR hack,我发现这更容易理解,而且它也是一个单行。

public static void swap(int[] arr, int i, int j) {
    arr[i] = (arr[i] + arr[j]) - (arr[j] = arr[i]);
}

我从一些原始基准测试中看到,性能差异也基本上可以忽略不计。

这是在不使用临时变量的情况下交换数组元素的标准方法之一,至少对于整数而言。

【讨论】:

  • 但是 arr[i] + arr[j] 可能会溢出
  • @ChaojunZhong 这应该不是问题。当减去另一个数字时,新的[i] 将计算得很好。在 IDE 中尝试
【解决方案4】:

如果你想交换字符串。这已经是有效的方法了。

但是,如果你想交换整数,你可以使用 XOR 来更有效地交换两个整数,如下所示:

int a = 1; int b = 2; a ^= b; b ^= a; a ^= b;

【讨论】:

  • 您使用了哪些测试来对此进行基准测试以及速度改进是什么?
  • @bhuang3 - 请修改您的帖子,解释为什么 Java 编译器不执行此优化。我当然怀疑它确实是 DV。
  • @djechlin 我不考虑编译器优化。我只是在谈论编程。
  • 那更长,更混乱,更难阅读并且需要更多的代码行。我认为仅就堆栈上声明的变量数量的资源而言,它更有效,但没有理由相信 OP 要求沿此资源进行优化,因此 DV 代表......
  • @Robin 不客气,但是,正如djechlin 所说,这种方式读起来很难理解,顺便说一句,如果两个整数指向相同的地址,可能会有一些问题,但是在java中,内存分配是由JVM处理的,所以这种方式是否实用很难说。
【解决方案5】:

使用Collections.swapArrays.asList

Collections.swap(Arrays.asList(arr), i, j);

【讨论】:

  • 如果数组中的类型是原始类型,这将不起作用。Arrays.asList(int[]) 会将其转换为 int[] 的列表。
  • 我能知道为什么它不适用于原始类型吗?实际上它对我不起作用需要知道原因。
【解决方案6】:

就地交换(如果你还不知道的话)可以通过不创建临时变量来节省一点空间。

arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];

【讨论】:

    【解决方案7】:

    迟到令人难以置信(我深表歉意),但可以实施比此处提供的更通用的解决方案(适用于原语和非原语):

    public static void swap(final Object array, final int i, final int j) {
        final Object atI = Array.get(array, i);
        Array.set(array, i, Array.get(array, j));
        Array.set(array, j, atI);
    }
    

    你失去了编译时的安全性,但它应该可以解决问题。

    注意 I:如果给定的 arraynull,您将获得一个 NullPointerException,如果给定的 array 不是一个数组,您将获得一个 IllegalArgumentException,以及一个 @如果任一索引对给定的array 无效,则为 987654327@。

    注意 II:为 每个 数组类型(Object[] 和所有原始类型)使用单独的方法会更高效(使用此处给出的其他方法),因为这需要一些装箱/拆箱.但它也需要编写/维护更多的代码。

    【讨论】:

      【解决方案8】:

      试试这个:

          int lowIndex = 0;
          int highIndex = elements.length-1;
      
          while(lowIndex < highIndex) {
              T lowVal = elements[lowIndex];
              T highVal = elements[highIndex];
              elements[lowIndex] = highVal;
              elements[highIndex] = lowVal;
      
              lowIndex += 1;
              highIndex -=1;
          }
      

      【讨论】:

      • 这试图回答什么问题?为什么这更可取?
      【解决方案9】:

      首先你不应该写for (int k = 0; k **&lt;** data.length **- 1**; k++)因为 for (int k = 0; k <= data.length - 1; k++) 2:for (int k = 0; k &lt; data.length; k++) 然后就可以正常工作了!!! 并交换您可以使用:将其中一个 int 保留在另一个地方,然后替换

      int x = data[k]
      data[k] = data[data.length - 1]
      data[data.length - 1] = x;
      

      因为你不想失去一个 int 的!!

      【讨论】:

      • 这试图回答什么问题?
      【解决方案10】:

      对象和原始类型的解决方案:

      public static final <T> void swap(final T[] arr, final int i, final int j) {
          T tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final boolean[] arr, final int i, final int j) {
          boolean tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final byte[] arr, final int i, final int j) {
          byte tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final short[] arr, final int i, final int j) {
          short tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final int[] arr, final int i, final int j) {
          int tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final long[] arr, final int i, final int j) {
          long tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final char[] arr, final int i, final int j) {
          char tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final float[] arr, final int i, final int j) {
          float tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      public static final void swap(final double[] arr, final int i, final int j) {
          double tmp = arr[i];
          arr[i] = arr[j];
          arr[j] = tmp;
      }
      

      【讨论】:

        【解决方案11】:

        这只是“hack”风格的方法:

        int d[][] = new int[n][n];
        
        static int swap(int a, int b) {
          return a;
        }
        ...
        
        in main class --> 
        
        d[i][j + 1] = swap(d[i][j], d[i][j] = d[i][j + 1])
        

        【讨论】:

          【解决方案12】:
          public static final void swap (int[] a, int i, int j) {
              a[i] = a[i] + a[j];
              a[j] = a[i] - a[j];
              a[i] = a[i] - a[j];
          }
          

          【讨论】:

          【解决方案13】:
          public class SwapElements {
          
          public static void main(String[] args) {
          
              int[] arr1 = new int[5];
              int[] arr2 = {10,20,30,40};
          
              System.out.println("arr1 Before Swapping " + Arrays.toString(arr1));
              System.out.println("arr2 Before Swapping " + Arrays.toString(arr2));
          
              int temp[];
              arr1[3] = 5;
              arr1[0] = 2;
              arr1[1] = 3;
              arr1[2] = 6;
              arr1[4] = 10;
          
              temp = arr1;
              arr1 = arr2;
              arr2 = temp;
              System.out.println("arr1 after Swapping " + Arrays.toString(arr1));
              System.out.println("arr2 after Swapping " + Arrays.toString(arr2));
          }
          

          }

          【讨论】:

          • 这不能回答问题。问题是交换数组的元素。翻转对两个数组的引用不会修改它们的内容。更不用说您使用与问题试图寻找替代方法完全相同的方法来切换数组。
          猜你喜欢
          • 2015-04-22
          • 2021-06-01
          • 1970-01-01
          • 1970-01-01
          • 2012-09-21
          • 1970-01-01
          • 2018-02-24
          • 1970-01-01
          相关资源
          最近更新 更多