在 Java 中交换两个字符串,通过将它们传递给实用程序函数,但不返回对象或使用包装类

Posted

技术标签:

【中文标题】在 Java 中交换两个字符串,通过将它们传递给实用程序函数,但不返回对象或使用包装类【英文标题】:Swap two strings in Java, by passing them to a utility function, but without returning objects or using wrapper classes 【发布时间】:2011-04-11 12:44:15 【问题描述】:

我正在尝试在 Java 中交换两个字符串。我从来没有真正理解过“字符串是不可变的”。理论上我理解它,但我在实践中从未遇到过。

另外,由于 String 在 Java 中是一个对象而不是原始类型,我不明白为什么下面的代码会打印两次相同的结果,而不是交换单词!

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;

我要打印出来

Hello World
World Hello

但它正在打印

Hello World
Hello World

我认为 s1 和 s2 是引用,因此应该交换引用,新的应该分别指向另一个。我哪里错了?

【问题讨论】:

【参考方案1】:

我认为 s1 和 s2 是引用,因此应该交换引用,新的应该分别指向另一个。

是的。本地内部 swap,这正是发生的事情。

但是,s1s2 是传递给函数的引用的副本,因此效果仍然是本地的。请注意,复制的不是字符串(因为String 是引用类型)。但是引用复制了。

...由于参数引用总是在 Java 中复制,因此根据您的规范编写 swap 函数很简单,是不可能的。

如果您在理解差异时遇到问题,请考虑以下问题:您想给朋友写一封信,然后将她的邮寄地址从您的地址簿复制到一个信封上。通过这个过程,你肯定没有复制她的家(复制整个房子在实践中有点困难)——你只是复制了地址。

嗯,地址她的家,所以它与 Java 引用完全一样。

【讨论】:

如何通过将两个字符串传递给另一个函数来交换两个字符串而不返回任何对象?在java中不可能吗? 直到现在我才理解“参考文献的副本”部分。谢谢:) @Senthil:对不起,忘了提。是的,用Java写这样的swap方法确实是不可能的。 我知道引用的概念。但我不知道有多少 java 不允许您使用它进行编程。谢谢你的解释:)【参考方案2】:

以下代码是NoGo,永远不要实现这种反模式,永远不要改变不可变对象的私有最终内部结构。不要责怪我展示了这个 hack,责怪 Oracle 的 jvm 没有禁止它。

但是,如果有一天你找到了一些可以运行的代码:

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

swap 的实现可能如下所示:(请自行承担风险,我警告过你!)

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

【讨论】:

这不会交换对象。它只是改变对象的内容【参考方案3】:

基本上,您不能在 Java 中实现 swap 方法。

您不能这样做的原因是 Java 参数具有按值传递的参数语义。因此,当您的 swap 方法将 s2 分配给 s1 等等时,它完全在 local 变量 s1s2 上运行,而不是在 s1 和调用方法main中的s2变量。

相比之下,如果您要在 C 中实现 swap 方法,它看起来像这样:

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

你会这样称呼它:

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

请注意,我们实际上是在传递“指向字符的指针”变量的地址。

在 Java 中,您不能这样做,因为您无法获取变量的地址。根本不支持。

【讨论】:

【参考方案4】:

最近有a very similar question 关于交换两个整数。而my answer then 是使用atomic references:

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

这当然不是很令人满意,因为您几乎没有针对 AtomicReferences 进行开发。但基本上你需要使用某种容器,因为你不能只交换引用。所以你可以例如交换一个二元素数组或列表的元素,但你不能在没有访问原始变量的情况下交换两个字符串(所以你不能在辅助函数中这样做)。

【讨论】:

问题说,没有使用包装类 @newacct 是的,但这在 Java 中是不可能的(正如您可以在接受的答案中看到的那样),所以我提供了一种解决方法。【参考方案5】:

Java Strings 是通过引用实现的,因此您需要交换它们的引用。

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

它会给你这个输出:

World Hello

【讨论】:

【参考方案6】:

函数交换中的 s1 和 s2 变量是该函数的本地变量。

您需要将 s1 和 s2 定义为您的类的私有变量,或者从您的函数返回一个字符串数组。

【讨论】:

【参考方案7】:

由于 Java 使用按值传递,您定义的交换函数不会进行任何类型的交换;指针 s1 和 s2 在本地交换,但交换的结果不会持久。您可以实现的最接近的事情是将要交换的项目包装在某个类中并在该类中定义一个交换方法。这将允许您在此类的实例之间交换数据,尽管它实际上不会交换基础数据。例如:

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

【讨论】:

【参考方案8】:
String s1 = "Hello";
String s2 = "World";
    s1=s1+" "+s2;
s2=s1.split(" ")[0];
s1=s1.split(" ")[1];
System.out.println(s1 + " " + s2);

【讨论】:

【参考方案9】:

通过使用StringBufferReaderStringBuilder 可以解决这个问题。首先从用户那里获取值,然后将它们拆分并将它们存储在数组中。以相反的顺序运行 for 循环,以便最后一个字符串将被附加并显示第一个,第一个将最后显示。 输入:你好世界 输出:

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

【讨论】:

【参考方案10】:

Java 将内存中对象的地址传递给函数。然后函数中的一些局部变量保存该地址。如果您将一些新地址(另一个对象)分配给这些局部变量,则原始对象没有任何改变的理由。

但是,对原始对象的字段所做的更改以及手头的地址将被应用。 (与 c/c++ 中的. 运算符有关)

This link 也可能有帮助。

【讨论】:

以上是关于在 Java 中交换两个字符串,通过将它们传递给实用程序函数,但不返回对象或使用包装类的主要内容,如果未能解决你的问题,请参考以下文章

如何在函数中交换作为指针传递的 2 个字符串?

汇编:遍历一系列字符并交换它们

Java并发:线程间数据传递和交换

如何在后台的两个 android 活动之间交换数据以通过蓝牙发送数据?

1247. 交换字符使得字符串相同(161_1)

两个数交换问题