Swap two strings in Java, by passing them to a utility function, but without returning objects or using wrapper classes
I am trying to swap two strings in Java. I never really understood "strings are immutable". I understand it in theory, but I 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开发者_JS百科(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. Where am I going wrong?
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.
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);
}
}
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.
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).
Java String
s are implemented with references, so you need to swap their references.
String s1 = "Hello";
String s2 = "World";
AtomicReference<String> String1 = new AtomicReference<String>(s1);
AtomicReference<String> String2 = new AtomicReference<String>(s2);
String1.set(String2.getAndSet(String1.get()));
System.out.println(String1 + " " + String2);
It will give you this output:
World Hello
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.
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")
String s1 = "Hello";
String s2 = "World";
s1=s1+" "+s2;
s2=s1.split(" ")[0];
s1=s1.split(" ")[1];
System.out.println(s1 + " " + s2);
By using StringBufferReader
and StringBuilder
this can be solved. At 1st get the values from the user then split them and store them in array. Run the for loop in the reverse order so that the last string will be appended and displayed 1st the first will displayed last.
input:hello world
output:
import java.io.*;
import java.util.*;
import java.lang.*;
public class Solution {
public static void main(String[] args) {
try {
int s,i,j;
StringBuilder str = new StringBuilder();
String st,t;
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
t = br.readLine();
s = Integer.parseInt(t);
for (i = 0; i < s; i++) {
st=br.readLine();
String st1[]=st.split("\\s+");
// System.out.println("l="+st1.length);
for (j = st1.length - 1; j >= 0; j--) {
str.append(st1[j] + " ");
}
System.out.println(str);
str.setLength(0);
}
} catch (Exception e) {
System.out.println(e);
}
}
}
Java passes the address of objects in the memory to functions. Then some local variable in the function holds that address. There is no reason for the original object to change if you assign some new address (another object) to those local variables.
However, changes to the fields of the original object, with the address in hand, ARE applied. (relate to .
operator in c/c++)
This link may also help.
精彩评论