Манипулирование перемещенными массивами изменяет данные основного массива в Java

#java

#java

Вопрос:

Я беру так:

  Collection<TaskComment> commentsCollection = task.getComments();
 ArrayList<TaskComment> comments = new ArrayList<>(commentsCollection);

       for(TaskComment cmnt : comments){

                cmnt.setUser(commentUser.getLogin());
                cmnt.setCreatedAtString(cmnt.getCreatedAt().format(DateTimeFormatter.ofPattern(Constants.DATE_TIME_FORMAT)));
            }
  

Здесь я беру комментарии и помещаю их в коллекцию, а затем в arraylist. Затем я манипулирую комментариями с помощью методов класса DTO.

Но когда я меняю cmnt , комментарии от task.getcomments меняются.

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

Я этого не делаю:

 repository.save(task);
  

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

 task.getComments();
  

вернуть измененные данные? Я не сохранил в базе данных?

Комментарии:

1. Я не делаю этого: repository.save (task); после того, как я манипулировал, поэтому он не должен сохраняться в базе данных? Итак, почему task.getComments(); приносит измененные данные? Я не сохранил в базе данных?

Ответ №1:

Это потому, что вы все еще смотрите на те же TaskComment объекты.

Ссылки на конкретный комментарий могут быть в двух коллекциях, commentsCollection и comments , но это только один объект. Не имеет значения, как вы получаете объект, если вы манипулируете им, он меняется.


Представьте свои объекты, в данном случае комментарии, в виде блоков. Вы привязываете красную строку к каждому блоку и передаете строки Алисе для хранения. Затем вы привязываете синюю строку к каждому блоку и передаете эти строки Бобу для хранения.
Теперь предположим, что Боб тянет за строку и получает поле. Он открывает коробку, кладет в нее шарик и засовывает коробку обратно на свое место.
Теперь Алиса тянет за строку для этого поля и получает поле. Если она откроет его, она найдет в нем шарик.


Что произойдет, если вы вызовете task.getComments(); снова, зависит от реализации. Если вы не сохраняли данные в базе данных и если этот метод получает их из базы данных каждый раз, вы не должны видеть никаких изменений в этих комментариях. Это как если бы вы получили новый набор блоков для Алисы.

Однако гораздо более вероятно, что когда вы загружаете Task из базы данных, она загружает связанные с ней комментарии, добавляя их как свойство задачи. Затем, при каждом вызове для получения комментариев, вы получаете их не из базы данных, а из объекта Task. И к этим комментариям привязаны строки как у Алисы, так и у Боба.

Комментарии:

1. Можем ли мы сказать, что это ссылка на объект, а затем манипуляция выполняется над самим объектом. ?

2. Итак, как я могу справиться с этим? Мне нужно передать во внешний интерфейс разные комментарии, но я не могу это изменить.

3. Я не делаю этого: repository.save (task); после того, как я манипулировал, поэтому он не должен сохраняться в базе данных? Итак, почему task.getComments(); приносит измененные данные? Я не сохранил в базе данных?

4. @SQB , поэтому я очень смущен, передается ли Java по значению или ссылке, потому что все говорят, что она передается по значению 🙂

Ответ №2:

Это происходит потому, что, выполнив приведенную ниже строку, вы получите коллекцию ссылок, которая содержит фактические объекты

 Collection<TaskComment> commentsCollection = task.getComments();
  

Нравится

 commentsCollection
  --> 0th element in commentsCollection --> (TaskComment object 1)
  --> 1st element in commentsCollection --> (TaskComment object 2)
  --> 2nd element in commentsCollection --> (TaskComment object 3)
  

Теперь, когда вы скопируете его в комментарии ArrayList

 ArrayList<TaskComment> comments = new ArrayList<>(commentsCollection);
  

но все равно это будет похоже

 comments
  --> 0th element in comments --> (TaskComment object 1)
  --> 1st element in comments --> (TaskComment object 2)
  --> 2nd element in comments --> (TaskComment object 3)
  

И теперь, когда вы пытаетесь получить доступ к своим объектам, вы фактически вносите изменения в объекты TaskComment (1, 2 и 3), вот почему task.getcomments() меняются.

Комментарии:

1. Итак, как я могу справиться с этим? Мне нужно передать во внешний интерфейс разные комментарии, но я не могу это изменить.

2. Если вам это нужно, поэтому вы отправляете его во внешний интерфейс, попробуйте создать новый класс ответов TaskCommentResponse (используя тот же TaskComment), а затем скопируйте каждый TaskComment в новый или используйте clone

3. Я не делаю этого: repository.save (task); после того, как я манипулировал, поэтому он не должен сохраняться в базе данных? Итак, почему task.getComments(); приносит измененные данные? Я не сохранил в базе данных?

4. Возможно, вы используете @Transactional, который автоматически сохраняет измененные объекты в БД.

5. Нет, он не сохраняется в БД. при следующем вызове снова появляются неизмененные значения. task.getcomments изменяется только временно, но я этого не понимаю.

Ответ №3:

В вашем примере вы создаете только 1 объект, task.getComments(); на который ссылаются обе commentsCollection и comments ссылочные переменные.

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

 Collection<TaskComment> commentsCollection = task.getComments();
ArrayList<TaskComment> comments = new ArrayList();
for(TaskComment cmnt : commentsCollection ){
    comments.add(cmnt.clone());
 }
  

Переопределите clone метод в TaskComment классе следующим образом:

 public Object clone() {
    //deep copy
    TaskComment newObject = new TaskComment();
    //Now copy each property of original object to the new object.
    //e.g. newObject.setProperty(this.getProperty()); 
    return newObject;
  }
  

Комментарии:

1.Итак, как я могу справиться с этим? Мне нужно передать во внешний интерфейс разные комментарии, но я не могу это изменить.

2. Я не делаю этого: repository.save (task); после того, как я манипулировал, поэтому он не должен сохраняться в базе данных? Итак, почему task.getComments(); приносит измененные данные? Я не сохранил в базе данных?

3. @отметьте, что вам нужно глубоко СКОПИРОВАТЬ ваш commentsCollection список в comments список.

Ответ №4:

В вашем коде фактический объект, т. е. commentsCollection ссылка, будет присвоен comments коллекции, и после этого вы повторяете эту коллекцию, и при повторении этого нового объекта коллекции, т. Е. comments , вы изменяете фактическую ссылку, т.Е. commentsCollection .

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

Комментарии:

1. Хороший первый ответ, но его можно улучшить, добавив образцы кода, где это возможно 🙂