Efficient swapping of elements of an array in Java
Asked Answered
U

13

81

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?

Understrapper answered 7/12, 2012 at 15:38 Comment(6)
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 hassleAckerman
If you can work with List instead of Array, you can use Collections.swap(List, i, j).Rozalin
@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?Understrapper
@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.Rozalin
@SergioNakanishi Thank you for the book tip. I just googled it and it seems pretty nice.Understrapper
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.Overriding
E
44

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).

Estrin answered 7/12, 2012 at 15:40 Comment(10)
Just for the record, what is the benefit in using static final instead of just static or even none?Understrapper
@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. :-)Estrin
I think you can't override a static method.Rozalin
@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?Understrapper
@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.Estrin
@T.J.Crowder: Thank you for the Lesson, this was new to me.Understrapper
@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 (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 (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: ideone.com/yXDgpa.Estrin
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.Gpo
Technically, static methods in sub classes are not overridden. They are hidden.Haematogenous
@DenisNikolaenko - Ah, yes, thanks! Lurkers, more here.Estrin
R
35

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);
}
Retriever answered 7/12, 2012 at 15:59 Comment(1)
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 .Vexatious
D
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.

Demarcusdemaria answered 28/1, 2017 at 15:13 Comment(2)
but arr[i] + arr[j] may overflowGriswold
@ChaojunZhong that should not be an issue. The new [i] will be calculated fine when the other number is subtracted. Try it in an IDEDemarcusdemaria
O
13

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;
Orthodontist answered 7/12, 2012 at 15:41 Comment(9)
What tests did you use to benchmark this and what were the speed improvements?Tardif
@Tardif at least, you don't need to create a temporary variableOrthodontist
XOR sounds very interesting to me, do you have any source or examples on this, however this won't help me in this case ;)Understrapper
@Orthodontist - please amend your post explaining why the Java compiler does not perform this optimization. I of course suspect it does hence the DV.Tardif
@Tardif I don't consider compiler optimization.. I'm just talking about programming.Orthodontist
@Understrapper int a = 1; int b = 2; a ^= b; b ^= a; a ^= b;Orthodontist
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...Tardif
@Tardif 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.Orthodontist
@Understrapper 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.Orthodontist
T
6

Use Collections.swap and Arrays.asList:

Collections.swap(Arrays.asList(arr), i, j);
Tonedeaf answered 20/9, 2017 at 5:40 Comment(2)
This will not work if the type in the array is primitive.Arrays.asList(int[]) will convert it to a list of int[].Obedient
Can I know why it will not work for primitive types. Actually its not working for me need to know the reason for this.Regale
M
6

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];
Migratory answered 21/1, 2021 at 0:44 Comment(0)
E
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.

Espionage answered 2/8, 2017 at 1:9 Comment(0)
D
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;
    }
Decorator answered 11/8, 2017 at 15:29 Comment(1)
What question does this try to answer? Why would this be preferable?Overriding
R
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!!

Recurrence answered 24/4, 2019 at 19:14 Comment(1)
What question does this try to answer?Overriding
W
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;
}
Westberg answered 15/6, 2019 at 21:49 Comment(0)
I
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])
Inquisitionist answered 11/8, 2020 at 1:34 Comment(0)
B
0
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];
}
Backstretch answered 22/9, 2021 at 10:3 Comment(1)
What's the distinguishing detail over Neel Alex' answer?Overriding
T
-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));
}

}

Terence answered 12/2, 2020 at 3:0 Comment(1)
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.Sgraffito

© 2022 - 2024 — McMap. All rights reserved.