79

I am wondering if there is a more efficient way of swapping two elements in an Array, than doing something like this:

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

Well, this is obviously not bad or even wrong, but I need to swap very often so I am interested if there are any Libs or something that provide a more efficient way to do this?

greybeard
  • 2,249
  • 8
  • 30
  • 66
Robin
  • 3,512
  • 10
  • 39
  • 73
  • For string? No not really. If you need to do it frequently you might as well just roll your own function once and then its not a hassle – Grambot Dec 07 '12 at 15:40
  • 4
    If you can work with List instead of Array, you can use Collections.swap(List, i, j). – Sergio Nakanishi Dec 07 '12 at 15:44
  • @Sergio Nakanishi: Interesting Point, maybe I will be able to switch to Collections, however I am not sure if pure Array usage wouldn't be faster? – Robin Dec 07 '12 at 15:47
  • 4
    @Robin: I usually prefer to use ArrayList over array. ArrayList uses an array internally and the overhead of calling a method to manipulate the array is not signicant. The book Effective Java has a more complete reasoning to choose List instead of an array. – Sergio Nakanishi Dec 07 '12 at 16:04
  • @SergioNakanishi Thank you for the book tip. I just googled it and it seems pretty nice. – Robin Dec 07 '12 at 16:13
  • That's about as effective as they come. There are differences in readability of alternatives. Measure (using a suitable framework in case of a microbenchmark) before assuming any difference in *efficiency*. – greybeard Nov 12 '21 at 17:14

13 Answers13

43

Nope. You could have a function to make it more concise each place you use it, but in the end, the work done would be the same (plus the overhead of the function call, until/unless HotSpot moved it inline — to help it with that, make the function static final).

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • 3
    Just for the record, what is the benefit in using `static final` instead of just `static` or even none? – Robin Dec 07 '12 at 15:55
  • 7
    @Robin: `static` tells the compiler and VM (remember, HotSpot is the VM, which does a lot of runtime optimization) that it doesn't have worry about setting up `this`, which makes it easier to inline the function. `final` says that it won't be overridden in a subclass. I could easily be wrong about the degree to which HotSpot cares about either of them, really, I'm not au courant with HotSpot's latest wizardry. :-) – T.J. Crowder Dec 07 '12 at 15:56
  • 3
    I think you can't override a static method. – Sergio Nakanishi Dec 07 '12 at 15:57
  • @T.J.Crowder: Thank you, I was aware of that, but didn't know/thought it would speed it up. I always thought, having a method static, would lead to a decrease performance, because the VW will load the method on startup? – Robin Dec 07 '12 at 16:01
  • 2
    @Robin: All methods in a class are loaded when the class is loaded, whether instance or static, more in [Section 12.4 of the JLS](http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4). – T.J. Crowder Dec 07 '12 at 16:04
  • @T.J.Crowder: Thank you for the Lesson, this was new to me. – Robin Dec 07 '12 at 16:06
  • 3
    @Sergio: *"I think you can't override a static method."* You can, unless it's `final`, but it's very different from overriding an instance method. Note this error overriding a `static final` (http://ideone.com/NuMyv4): "error: foo() in Derived cannot override foo() in Base...overridden method is static,final". That error goes away if it's not `final` (http://ideone.com/vnuAk6). It only comes into play if you call the static method via an *instance reference*, which is an odd thing to do. The method called is determined at compile-time, from the type of the ref: http://ideone.com/yXDgpa. – T.J. Crowder Jan 05 '16 at 10:25
  • In modern JVMs it does not matter whether the method is `final` or not; using Class Hierarchy Analysis the compiler determines how many overrides there are and inlines it right away as long as the bytecode-length of the method is shorter than 35 bytes (`-XX:MaxInlineSize`). If it loads class with override, it invalidates its assumptions and recompiles the method again. Use `final` when you want to prevent overriding (here it makes sense), not just to hint the assembly compiler. – Radim Vansa Mar 17 '16 at 11:30
  • 2
    Technically, static methods in sub classes are not overridden. They are hidden. – Denis Nikolaenko Mar 28 '20 at 22:11
  • @DenisNikolaenko - Ah, yes, thanks! Lurkers, [more here](https://docs.oracle.com/javase/specs/jls/se13/html/jls-8.html#jls-8.4.8.2). – T.J. Crowder Mar 29 '20 at 08:30
36

This should make it seamless:

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);
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
  • 3
    this works well , thanks . note that this requires an array of objects so if you get on of those weird compile time errors make sure your using `Integer[] array` instead of `int[] array` . – LostNomad311 May 18 '16 at 21:09
19

If you're swapping numbers and want a concise way to write the code without creating a separate function or using a confusing XOR hack, I find this is much easier to understand and it's also a one liner.

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

What I've seen from some primitive benchmarks is that the performance difference is basically negligible as well.

This is one of the standard ways for swapping array elements without using a temporary variable, at least for integers.

kmecpp
  • 2,371
  • 1
  • 23
  • 38
12

If you want to swap string. it's already the efficient way to do that.

However, if you want to swap integer, you can use XOR to swap two integers more efficiently like this:

int a = 1; int b = 2; a ^= b; b ^= a; a ^= b;
Mark
  • 1,788
  • 1
  • 22
  • 21
bhuang3
  • 3,493
  • 2
  • 17
  • 17
  • 3
    What tests did you use to benchmark this and what were the speed improvements? – djechlin Dec 07 '12 at 15:42
  • @djechlin at least, you don't need to create a temporary variable – bhuang3 Dec 07 '12 at 15:43
  • XOR sounds very interesting to me, do you have any source or examples on this, however this won't help me in this case ;) – Robin Dec 07 '12 at 15:44
  • 1
    @bhuang3 - please amend your post explaining why the Java compiler does not perform this optimization. I of course suspect it does hence the DV. – djechlin Dec 07 '12 at 15:45
  • 1
    @djechlin I don't consider compiler optimization.. I'm just talking about programming. – bhuang3 Dec 07 '12 at 15:46
  • @Robin int a = 1; int b = 2; a ^= b; b ^= a; a ^= b; – bhuang3 Dec 07 '12 at 15:48
  • 2
    That's far longer, more confusing, harder to read and takes more lines of code. I suppose it's more efficient solely with regard to the resource of number of variables declared on the stack but there is no reason to believe the OP was asking for optimization along this resource so DV stands... – djechlin Dec 07 '12 at 15:50
  • @djechlin yes, you're right, it's hard to understand and read, it's just a programming tricky. In the real programming development, I prefer to use the obvious way. – bhuang3 Dec 07 '12 at 15:52
  • 3
    @Robin you're welcome, however, just as `djechlin` says, this way is hard to understand an read, it's just a programming tricky, by the way, if the two integers point to the same address, it may have some problems, but in java, the memory allocation is handled by JVM, so it hard to say if this way is practical or not. – bhuang3 Dec 07 '12 at 15:56
5

Use Collections.swap and Arrays.asList:

Collections.swap(Arrays.asList(arr), i, j);
ZhekaKozlov
  • 36,558
  • 20
  • 126
  • 155
5

inplace swapping (incase you already didn't know) can save a bit of space by not creating a temp variable.

arr[i] = arr[i] + arr[j];
arr[j] = arr[i] - arr[j];
arr[i] = arr[i] - arr[j];
Neel Alex
  • 605
  • 6
  • 10
1

Incredibly late to the party (my apologies) but a more generic solution than those provided here can be implemented (will work with primitives and non-primitives alike):

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);
}

You lose compile-time safety, but it should do the trick.

Note I: You'll get a NullPointerException if the given array is null, an IllegalArgumentException if the given array is not an array, and an ArrayIndexOutOfBoundsException if either of the indices aren't valid for the given array.

Note II: Having separate methods for this for every array type (Object[] and all primitive types) would be more performant (using the other approaches given here) since this requires some boxing/unboxing. But it'd also be a whole lot more code to write/maintain.

BeUndead
  • 3,463
  • 2
  • 17
  • 21
1

Try this:

    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;
    }
J.user94
  • 81
  • 11
1
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];
}
Anil
  • 133
  • 1
  • 8
  • What's the distinguishing detail over [Neel Alex' answer](https://stackoverflow.com/a/65819684/3789665)? – greybeard Nov 12 '21 at 17:20
0

first of all you shouldn't write for (int k = 0; k **<** data.length **- 1**; k++)because the < is until the k is smaller the length -1 and then the loop will run until the last position in the array and won't get the last place in the array; so you can fix it by two ways: 1: for (int k = 0; k <= data.length - 1; k++) 2: for (int k = 0; k < data.length; k++) and then it will work fine!!! and to swap you can use: to keep one of the int's in another place and then to replace

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

because you don't want to lose one of the int's!!

viper-zero
  • 41
  • 1
  • 1
  • 9
0

Solution for object and primitive types:

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;
}
Error404
  • 719
  • 9
  • 30
0

This is just "hack" style method:

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])
ellerymoon
  • 35
  • 6
-3
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));
}

}

  • 2
    This doesn't answer the question. The question was to swap elements of an array. Flipping the references to two arrays does nothing to modify their contents. Not to mention you used the exact same method to switch the arrays as the question was trying to find an alternative to. – Locke Feb 12 '20 at 04:12