views:

265

answers:

6

I am trying to swap two strings in Java. I never really understood "strings are immutable". I understand in theory but never came across it in practice.

Also, since String is an object in Java and not a primitive type, I don't understand why the following code prints the same result twice, instead of interchanging the words!

public static void main(String[] args)
{
    String s1 = "Hello";
    String s2 = "World";

    System.out.println(s1 + " " + s2);

    Swap(s1, s2);

    System.out.println(s1 + " " + s2);
}

public static void Swap(String s1, String s2)
{
    String temp = s1;
    s1 = s2;
    s2 = temp;
}

I want it to print

Hello World
World Hello

But it is printing

Hello World
Hello World

I thought s1 and s2 are references and hence the references should be swapped and the new ones should point to the other one respectively. Can someone explain where I am going wrong?

+10  A: 

I thought s1 and s2 are references and hence the references should be swapped and the new ones should point to the other one respectively.

Yes. Locally inside swap, this is exactly what happens.

However, s1 and s2 are copies of the references passed into the function, so the effect remains local. Note that it’s not the strings that are copied (since String is a reference type). But the references are copied.

… and since parameter references are always copied in Java, writing a swap function according to your specifications is, quite simply, not possible.

If you have problems understanding the difference, consider this: you want to write a letter to a friend so you copy her postal address from your address book onto an envelope. By this process, you certainly didn’t copy her home (copying a whole house is a bit difficult in practice) – you only copied the address.

Well, an address refers to her home so it’s exactly like a Java reference.

Konrad Rudolph
How can I swap two strings, without returning any objects, by passing them to another function? Is it not possible in java?
Senthil
Only now I understood the "copies of the references" part. Thanks :)
Senthil
@Senthil: sorry, forgot to mention that. Yes, writing such a `swap` method in Java is indeed impossible.
Konrad Rudolph
I am aware of references concept. But I am not aware of how much java does NOT allow you to program using it. Thanks for the explanation :)
Senthil
+4  A: 

Basically, you cannot implement swap method in Java.

The reason you cannot do this is that Java argument has pass-by-value argument semantics. So when your swap method assigns s2 to s1 and so on, it is operating entirely on the local variables s1 and s2, and NOT on the s1 and s2 variables in the calling method main.

By contrast, if you were to implement the swap method in C, it would look something like this:

void swap(char ** s1, char ** s2) {
    char * temp = *s1;
    *s1 = *s2;
    *s2 = temp;
}

and you would call it like this:

char *s1 = "Hello World";
char *s2 = "Goodbye World";
swap(&s1, &s2);

Notice that we are actually passing the address of a "pointer to char" variable.

In Java, you cannot do this because you cannot take the address of a variable. It is simply not supported.

Stephen C
+1 (15 characters)
Senthil
+1  A: 

The s1 and s2 variables in your function swap are local to that function.

You need to define s1 and s2 as private variables for your class, or return an array of strings from your function.

James Anderson
+1  A: 

Since Java uses pass-by-value, the swap function that you have defined does not do any sort of swapping; the pointers s1 and s2 are swapped locally, but the result of the swap does not persist. The closest thing you can achieve is to wrap the items you want to swap in some class and define a swap method in that class. This will allow you to swap data between instances of this class, although it will not actually swap the underlying data. As an example of this:

  public class Swapper<T> {
      public Swapper(T obj) {
             data_ = obj;
      }
      public T get() { return data_; }
      public void set(T value) { data_ = value; }
      public void swap(Swapper<T> o) {
           T tmp = o.data_;
           o.data_ = data_;
           data_ = tmp;
      }
      private T data_ = null;
  }

  // .. Now you can do:
  Swapper<String> a = new Swapper("Hello");
  Swapper<String> b = new Swapper("World");
  // a.get().equals("Hello")
  a.swap(b); // now a.get().equals("World")
Michael Aaron Safyan
+2  A: 

There was a very similar question recently about swapping two ints. And my answer then was to use atomic references:

public void swap(AtomicReference<String> a, AtomicReference<String> b){
    // update: look mom, no variables
    a.set(b.getAndSet(a.get()));
}

Of course this not very satisfactory, as you hardly ever develop against AtomicReferences. But basically you need to use some kind of container because you can't just swap the references. So you can e.g. swap the elements of a two-element array or list, but you just can't swap two strings without having access to the original variables (so you can't do it in a helper function).

seanizer
+3  A: 

The following code is a NoGo, never implement this anti pattern, never ever ever ever change private final internals on immutable objects. Don't blame me for showing this hack, blame Oracle's jvm for not disallowing it.

But, if one day you find some code where this works:

 String a = "Hello";
 String b = "World";
 String c = "World";
 swap(a,b);
 System.out.printf("%s %s%n", a,b);  // prints: World Hello !!

 System.out.println(c);  // Side effect: prints "Hello" instead of "World"....

the implementation of swap could look like this: (read on your own risk, I warned you!)

 private void swap(String a, String b) {
   try {
     Field value = String.class.get("value");
     value.setAccessible(true);    // this should be forbidden!!

     char[] temp = value.get(a);
     value.set(a, value.get(b));
     value.set(b, temp);           // Aaargh, please forgive me
   } catch(Exception e) {
     e.printStackTrace(e);
   }
 }
Andreas_D
+1, that's some powerful magic :)
MAK
+1... and your comments are awesome.. LOLLLLL
Senthil