Распределение списков Java [дубликат]

BFS очень хорош для кратчайших путей, а DFS - нет.

68
задан brimborium 22 August 2012 в 14:26
поделиться

16 ответов

Каждая переменная в Java является ссылкой. Поэтому, когда вы делаете

SomeClass s2 = s1;

, вы просто указываете s2 на тот же объект, что и s1. Фактически вы назначаете значение ссылки s1 (которое указывает на экземпляр SomeClass) на s2. Если вы измените s1, будет также изменен s2 (потому что он указывает на тот же объект).

Существует исключение, примитивные типы: int, double, float, boolean, char, byte, short, long. Они хранятся по значению. Поэтому при использовании = вы присваиваете значение только, но не можете указывать на один и тот же объект (потому что это не ссылки). Это означает, что

int b = a;

устанавливает значение b в значение a. Если вы измените a, b не изменится.

В конце дня все назначается по значению, это всего лишь значение ссылки, а не значение объекта (с исключение примитивных типов, как указано выше).

Итак, в вашем случае, если вы хотите сделать копию s1, вы можете сделать это следующим образом:

SomeClass s1 = new SomeClass("first");
SomeClass s2 = new SomeClass(s1.getText());

Кроме того, вы можете добавить конструктор копирования к SomeClass, который принимает экземпляр в качестве аргумента и копирует его в свой собственный экземпляр.

class SomeClass {
  private String text;
  // all your fields and methods go here

  public SomeClass(SomeClass copyInstance) {
    this.text = new String(copyInstance.text);
  }
}

С помощью этого вы можете легко скопировать объект:

SomeClass s2 = new SomeClass(s1);
127
ответ дан 14 revs, 3 users 97% 15 August 2018 в 17:07
поделиться
  • 1
    +1 ссылки и примитивы копируются по значению. Возникает путаница, в которой вы не различаете ссылку и ссылку на объект. ;) – Peter Lawrey 22 August 2012 в 13:13
  • 2
    s1.clone() вариант иногда . – Ryan Amos 22 August 2012 в 14:59
  • 3
    вы можете использовать s1.clone () для копирования объекта в s2, он не передаст ссылку на s2, а скопирует данные с s1 на s2. – Mayur Raiyani 22 August 2012 в 15:45
  • 4
  • 5
    Каждая переменная в Java хранит значение . И поскольку классы являются ссылочными типами, значение в переменной типа класса является ссылкой . Когда вы выполняете задание, как в первом утверждении в этом ответе или в вопросе, вы копируете reference по значению . Вы ничего не копируете по ссылке. – Adam Robinson 22 August 2012 в 21:03

Вторая строка (SomeObject s2 = s1;) просто назначает вторую переменную первой. Это приводит к тому, что вторая переменная указывает на тот же экземпляр объекта, что и первый.

1
ответ дан 01es 15 August 2018 в 17:07
поделиться
Ответ

@ brimborium очень хорош (+1 для него), но я просто хочу подробнее рассказать об этом, используя некоторые цифры. Давайте сначала возьмем примитивное назначение:

int a = new Integer(5);
int b = a;
b = b + b;
System.out.println(a); // 5 as expected
System.out.println(b); // 10 as expected
int a = new Integer(5);

1- Первый оператор создает объект Integer значения 5. Затем, назначив его переменной a, объект Integer будет распакован и сохраняются в a в качестве примитива.

После создания объекта Integer и перед назначением:

enter image description here [/g0]

После назначения:

enter image description here [/g1]

int b = a;

2- Это только что прочитает значение a, а затем сохранит его в b.

(Объект Integer теперь имеет право на сбор мусора, но не обязательно собирает мусор в этой точке)

enter image description here [/g2]

b = b + b;

3- Это дважды читает значение b, добавляет их вместе и помещает новое значение в b.

enter image description here [/g3]


На другая сторона:

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;
s2.setText("second");
System.out.println(s1.getText()); // second as UNexpected
System.out.println(s2.getText()); // second as expected
SomeObject s1 = new SomeObject("first");

1- Создает новый экземпляр класса SomeObject и назначает его ссылке s1.

enter image description here [/g4]

SomeObject s2 = s1;

2- Это приведет к тому, что ссылка s2 указывает на объект, на который указывает s1. [/ g2 1]

enter image description here [/g5]

s2.setText("second");

3- Когда вы используете сеттеры для ссылки, он будет изменять объект, на который ссылается ссылка.

enter image description here [/g6]

System.out.println(s1.getText());
System.out.println(s2.getText());

4- Оба должны печатать second, так как две ссылки s1 и s2 относятся к одному и тому же объекту (как показано в предыдущем рисунок).

38
ответ дан assylias 15 August 2018 в 17:07
поделиться
  • 1
    @assylias Спасибо, я обновил свой ответ :) – Eng.Fouad 17 September 2012 в 09:43
  • 2
    Прохладный, +1 за ваш ответ. Хотя я хочу предложить некоторые дополнения: 1) изображения могут быть немного меньше. 2) Вы должны добавить ответ на свой другой вопрос (как дублировать SomeObject), возможно, также с изображениями. – brimborium 18 September 2012 в 09:15
  • 3
    Хороший ответ. Спасибо. – Devendra 12 December 2013 в 16:23

Когда вы назначаете и подвергаете возражению переменной, вы действительно назначаете ссылку на этот объект. Поэтому обе переменные s1 и s2 ссылаются на один и тот же объект.

1
ответ дан Averroes 15 August 2018 в 17:07
поделиться

Поскольку объекты ссылались на ссылку. Поэтому, если вы пишете s2 = s1, будет скопирована только ссылка. Как и в C, вы только обрабатывали адреса памяти. И если вы скопируете адрес памяти и измените значение за этим адресом, оба указателя (ссылки) изменят одно значение в этой точке памяти.

1
ответ дан BigAl 15 August 2018 в 17:07
поделиться

Когда вы пишете:

SomeObject s1 = new SomeObject("first");

s1 не является SomeObject. Это ссылка на объект SomeObject.

Следовательно, если вы назначаете s2 в s1, вы просто назначаете ссылки / дескрипторы, а базовый объект тот же. Это важно в Java. Все передается по значению, но вы никогда не пропускаете объекты вокруг - только ссылки на объекты.

Поэтому, когда вы назначаете s1 = s2, а затем вызываете метод на s2, который меняет объект, базовый объект изменяется, и это видно, когда вы ссылаетесь на объект через s1.

Это один из аргументов для неизменяемости в объектах. Делая объекты неизменными, они не будут меняться под вами и, таким образом, будут вести себя более предикативно. Если вы хотите скопировать объект, самым простым / наиболее практичным методом является написать метод copy(), который просто создает новую версию и копирует ее по полям. Вы можете делать умные копии с использованием сериализации / отражения и т. Д., Но это более сложно, очевидно.

10
ответ дан Brian Agnew 15 August 2018 в 17:07
поделиться
  • 1
    +1 для упоминания понятия неизменности. Это, наряду с концепцией ссылок, является ключевым в этой дискуссии. – Marvo 28 August 2012 в 18:47

Когда вы делаете это

SomeObject s1 = new SomeObject("first");
SomeObject s2 = s1;

, у вас есть 2 ссылки на один и тот же объект. Это означает, что какой бы ссылочный объект вы ни использовали, сделанные вами изменения будут видны при использовании второй ссылки.

Подумайте об этом так: у вас есть один телевизор в комнате, но два пульта: что вы используете, вы все равно будете вносить изменения в один и тот же базовый объект (телевизор).

15
ответ дан davek 15 August 2018 в 17:07
поделиться
  • 1
    +1, мне нравится телевизионная дистанционная аналогия. Я думаю, что примитивы - это телевизоры без пульта, где вам нужно физически перейти к фактическому набору и изменить значение ... или что-то еще? :) – Alex 22 August 2012 в 14:56

Измените свой класс, чтобы создать новую ссылку вместо того, чтобы использовать одну:

public class SomeObject{

    public String text;

    public SomeObject(String text){
        this.setText(text);
    }

    public String getText(){
        return text;
    }   

    public void setText(String text){
        this.text = new String(text);
    }
}

Вы можете использовать что-то вроде этого (я не претендую на идеальное решение):

public class SomeObject{

    private String text;

    public SomeObject(String text){
        this.text = text;
    }

    public SomeObject(SomeObject object) {
        this.text = new String(object.getText());
    }

    public String getText(){
        return text;
    }   

    public void setText(String text){
        this.text = text;
    }
}

Использование:

SomeObject s1 = new SomeObject("first");
SomeObject s2 = new SomeObject(s1);
s2.setText("second");
System.out.println(s1.getText()); // first
System.out.println(s2.getText()); // second
2
ответ дан Dragon 15 August 2018 в 17:07
поделиться

Привязка объекта к ссылке не клонирует ваш объект. Ссылки похожи на указатели. Они указывают на объект, и когда операции вызывается, это делается на объекте, указанном указателем. В вашем примере s1 и s2 указывают на один и тот же объект, а сеттеры изменяют состояние одного и того же объекта, а изменения видны через ссылки.

2
ответ дан Drona 15 August 2018 в 17:07
поделиться
int a = new Integer(5) 

В этом случае создается новое целое число. Здесь Integer является не примитивным типом, а значение внутри него преобразуется (в int) и присваивается int 'a'.

SomeObject s1 = new SomeObject("first");  
SomeObject s2 = s1;

В этом случае оба s1 и s2 являются ссылочными типами. Они не созданы, чтобы содержать значение, подобное примитивным типам, скорее они содержат ссылку на некоторый объект. Для понимания мы можем рассматривать ссылку как ссылку, которая указывает, где я могу найти ссылку на объект.

Здесь ссылка s1 сообщает нам, где мы можем найти значение «first» (которое на самом деле хранится в памяти компьютера в экземпляре SomeObject). В других словах s1 - это адрес объекта класса «SomeObject». Следующим присваиванием -

SomeObject s2 = s1;

мы просто копируем значение, хранящееся в s1-s2, и теперь мы знаем, что s1 содержит адрес строки «первым». После этого экземпляра println () создает тот же результат, потому что оба s1 и s2 переопределяют один и тот же объект.

Наряду с конструктором копирования вы можете скопировать объект с помощью метода clone (), если вы пользователь java. Клон можно использовать следующим образом:

SomeObject s3 = s1.clone(); 

Для получения дополнительной информации о clone () это полезная ссылка http://en.wikipedia.org/wiki/Clone_%28Java_method% 29

2
ответ дан HassanF 15 August 2018 в 17:07
поделиться

Начнем с вашего второго примера:

Этот первый оператор назначает новый объект s1

SomeObject s1 = new SomeObject("first");

Когда вы выполняете присвоение во втором выражении (SomeObject s2 = s1), вы говорите, что s2 указывает на тот же объект s1, который в данный момент указывает, поэтому у вас есть две ссылки на один и тот же объект.

Обратите внимание, что вы не дублировали SomeObject , а две переменные просто указывают на один и тот же объект. Поэтому, если вы изменяете s1 или s2, вы фактически изменяете один и тот же объект (обратите внимание, если вы сделали что-то вроде s2 = new SomeObject("second"), они теперь будут указывать на разные объекты).

В вашем первом Например, a и b являются примитивными значениями, поэтому изменение одного не повлияет на другое.

Под капотом Java все объекты работают с использованием pass by value. Для объектов вы передаете «значение», которое вы передаете, - это местоположение объекта в памяти (поэтому он, похоже, имеет аналогичный эффект прохождения по ссылке). Примитивы ведут себя по-разному и просто передают копию значения.

2
ответ дан Jeff Storey 15 August 2018 в 17:07
поделиться

В вашем коде s1 и s2 есть тот же объект (вы только создали один объект, используя new), и вы позволяете s2 указывать на тот же объект в следующем линия. Поэтому, когда вы меняете text, он меняет оба значения, если вы ссылаетесь на значение через s1 и s2.

Оператор + на целых объектах создает объект new он не изменяет существующий объект (поэтому добавление 5 + 5 не дает 5 нового значения 10 ...).

2
ответ дан Mathias Schwarz 15 August 2018 в 17:07
поделиться

Ответы, приведенные выше, объясняют поведение, которое вы видите.

В ответ на «Также, как дублировать SomeObject, если простое назначение не выполняет эту работу?» - попробуйте найти cloneable (это интерфейс Java, который обеспечивает один способ копирования объектов) и «copy constructors» (альтернативный и, возможно, лучший подход)

2
ответ дан matt freake 15 August 2018 в 17:07
поделиться

Это потому, что JVM хранит указатель на s1. Когда вы вызываете s2 = s1, вы в основном говорите, что указатель s2 (т. Е. Адрес памяти) имеет то же значение, что и для s1. Поскольку они оба указывают на одно и то же место в памяти, они представляют собой одно и то же.

Оператор = присваивает значения указателя. Он не копирует объект.

Объекты клонирования - это сложный вопрос сам по себе. У каждого объекта есть метод clone (), который может возникнуть у вас, но он делает мелкую копию (что в основном означает, что она делает копию объекта верхнего уровня, но любой объект, содержащийся в нем, не клонируется). Если вы хотите поиграть с копиями объектов, обязательно прочитайте Эффективное Java Джошуа Блоха

.
2
ответ дан mprivat 15 August 2018 в 17:07
поделиться

Это потому, что s1 и s2 работают только как ссылки на ваши объекты. При назначении s2 = s1 вы назначаете только ссылку, что означает, что оба будут указывать на один и тот же объект в памяти (объект, который имеет текущий текст «первым»).

Когда вы сейчас устанавливаете сеттер, на s1 или s2, оба будут изменять один и тот же объект.

1
ответ дан Raul Rene 15 August 2018 в 17:07
поделиться

Из Десять ошибок Программисты Java Создают :

6 - Путаница при передаче по значению и передача по ссылке

Это может быть неприятной проблемой для диагностики, потому что, когда вы смотрите на код, вы можете быть уверены, что его передача по ссылке, но обнаруживает, что ее фактически передают по значению. Java использует оба метода, поэтому вам нужно понять, когда вы проходите по значению, и когда вы проходите по ссылке.

Когда вы передаете примитивный тип данных, например char, int, float, или дважды, к функции, то вы проходите по значению. Это означает, что копия типа данных дублируется и передается функции. Если функция хочет изменить это значение, она будет изменять только копию. Как только функция завершится, и управление вернется к возвращаемой функции, «реальная» переменная останется нетронутой, и никаких изменений не будет сохранено. Если вам нужно изменить примитивный тип данных, сделайте его возвращаемым значением для функции или оберните его внутри объекта.

Поскольку int является примитивным типом, int b = a; копия по значению, что означает, что a и b представляют собой два разных объекта, но с тем же значением.

SomeObject s2 = s1; делают s1 и s2 две ссылки одного и того же объекта , поэтому, если вы измените один, другой будет изменен.

Хорошим решением является реализация другого конструктора, подобного этому:

public class SomeObject{

    public SomeObject(SomeObject someObject) {
        setText(someObject.getText());
    }

    // your code

}

Затем используйте его следующим образом:

SomeObject s2 = new SomeObject(s1);
4
ответ дан sp00m 15 August 2018 в 17:07
поделиться
  • 1
    Ссылка, которую вы указали в «Десяти ошибках Java-программистов», не подходит в этом контексте, так как он говорит о примитивах, а не о ссылках на объекты. – Drona 22 August 2012 в 13:18
Другие вопросы по тегам:

Похожие вопросы: