Поменяйте местами две строки в Java, передав их в служебную функцию, но без возврата объектов или использования классов-оболочек - PullRequest
8 голосов
/ 13 сентября 2010

Я пытаюсь поменять две строки в 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 являются ссылками, и поэтому ссылки следует поменять местами, а новые должны указывать на другую соответственно. Куда я иду не так?

Ответы [ 9 ]

13 голосов
/ 13 сентября 2010

Я думал, что s1 и s2 являются ссылками, и, следовательно, ссылки следует поменять местами, а новые должны указывать на другую соответственно.

Да.Локально внутри swap, это именно то, что происходит.

Однако s1 и s2 являются копиями ссылок, переданных в функцию, поэтомуэффект остается локальным.Обратите внимание, что копируются не строки (поскольку String является ссылочным типом).Но ссылки копируются .

… и поскольку ссылки на параметры всегда копируются в Java, написание функции swap в соответствии с вашими требованиями, довольно просто,невозможно.

Если у вас возникли проблемы с пониманием разницы, учтите следующее: вы хотите написать письмо другу, чтобы скопировать его почтовый адрес из адресной книги в конверт.Благодаря этому процессу вы, конечно, не копировали ее дом (на практике копировать весь дом довольно сложно) - вы только скопировали адрес.

Ну, адрес относится к ней домой, так что это точно как ссылка на Java.

6 голосов
/ 13 сентября 2010

Следующий код - NoGo , никогда не реализуйте этот анти-шаблон, никогда не изменяйте закрытые финальные внутренние элементы неизменяемых объектов. Не обвиняйте меня в том, что я показал этот взлом, обвиняйте jvm Oracle в том, что он не запрещает это.

Но, если однажды вы найдете какой-то код, где это работает:

 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 голосов
/ 13 сентября 2010

В принципе, вы не можете реализовать метод swap в Java.

Причина, по которой вы не можете сделать это, состоит в том, что аргумент Java имеет семантику аргумента передачи по значению.Поэтому, когда ваш swap метод присваивает s2 s1 и т. Д., Он полностью работает с локальными переменными s1 и s2, а НЕ с s1 и s2 переменные в вызывающем методе main.

Напротив, если бы вы реализовали метод swap в C, он бы выглядел примерно так:

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

ивы бы назвали это так:

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

Обратите внимание, что мы фактически передаем адрес переменной "pointer to char".

В Java вы не можете сделать это, потому что вы не можете взятьадрес переменной.Это просто не поддерживается.

2 голосов
/ 28 февраля 2012

Java String s реализованы со ссылками, поэтому вам нужно поменять их местами.

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
2 голосов
/ 13 сентября 2010

Был очень похожий вопрос недавно о замене двух целых.И мой ответ тогда заключался в использовании атомных ссылок :

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

Конечно, это не очень удовлетворительно, поскольку вы вряд ли когда-либо будете развиваться против AtomicReferences.Но в основном вам нужно использовать какой-то контейнер, потому что вы не можете просто поменять местами ссылки.Например, вы можете поменять местами элементы двухэлементного массива или списка, но вы просто не можете поменять две строки, не имея доступа к исходным переменным (поэтому вы не можете сделать это в вспомогательной функции).

1 голос
/ 29 августа 2012
String s1 = "Hello";
String s2 = "World";
    s1=s1+" "+s2;
s2=s1.split(" ")[0];
s1=s1.split(" ")[1];
System.out.println(s1 + " " + s2);
1 голос
/ 13 сентября 2010

Поскольку в 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")
1 голос
/ 13 сентября 2010

Переменные s1 и s2 в вашей функции подкачки являются локальными для этой функции.

Вам необходимо определить s1 и s2 как частные переменные для вашего класса или вернуть массив строк из вашей функции.

0 голосов
/ 26 ноября 2015

Используя StringBufferReader и StringBuilder, это можно решить.Сначала получите значения от пользователя, затем разделите их и сохраните в массиве.Запустите цикл 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);
    }
  }
}
...