如何在 Java 中使用 objects 正确复制 collections
How to correctly copy collections with objects in Java
我有一个方法 (List<Book> getListWithPrefixInName(List<Book> books)
) 接受 collection 作为输入并对每个 collection object 执行转换。而且我不希望这些变化反映在转移collection上,只是为了return修改collection。因为我需要我原来的collection.
因此,我想通过创建一个新的 collection:
来复制
List<Book> clone = new ArrayList<>(books);
可是我怎么错了...
我的代码:
public class App {
public static final String PREFIX = "PREFIX";
public static void main(String[] args) {
List<Book> books = new ArrayList<>();
books.add(Book.of(1L, "The Catcher in the Rye"));
books.add(Book.of(2L, "The Green Mile"));
List<Book> booksWithPrefix = getListWithPrefixInName(books);
for (Book book : books) {
if (book.getName().contains(PREFIX)) {
System.out.println(String.format("original book: '%s' have been changed", book));
}
}
}
public static List<Book> getListWithPrefixInName(List<Book> books) {
List<Book> clone = new ArrayList<>(books); // I thought that this cloning would be enough
return clone.stream()
.peek(b -> b.setName(PREFIX + ": " + b.getName()))
.collect(toList());
}
}
class Book {
private Long id;
private String name;
private Book(Long id, String name) {
this.id = id;
this.name = name;
}
public static Book of(Long id, String name) {
return new Book(id, name);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Book{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(id, book.id) &&
Objects.equals(name, book.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
因此我的整个原collection都被改变了,太糟糕了。
所以我不得不通过 third-party 库使用深度克隆:
import org.apache.commons.lang3.SerializationUtils;
public class App {
public static final String PREFIX = "PREFIX";
public static void main(String[] args) {
List<Book> books = new ArrayList<>();
books.add(Book.of(1L, "The Catcher in the Rye"));
books.add(Book.of(2L, "The Green Mile"));
List<Book> booksWithPrefix = getListWithPrefixInName(books);
for (Book book : books) {
if (book.getName().contains(PREFIX)) {
System.out.println(String.format("original book: '%s' have been changed", book));
}
}
}
public static List<Book> getListWithPrefixInName(List<Book> books) {
return books.stream()
.map(b -> SerializationUtils.clone(b)) // deep clone
.peek(b -> b.setName(PREFIX + ": " + b.getName()))
.collect(toList());
}
}
class Book implements Serializable {
private Long id;
private String name;
private Book(Long id, String name) {
this.id = id;
this.name = name;
}
public static Book of(Long id, String name) {
return new Book(id, name);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Book{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(id, book.id) &&
Objects.equals(name, book.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
这个问题有没有更优雅的解决方案?还是使用深度克隆是唯一正确的解决方案?
在流中更改状态(就像您在 peek
中所做的那样)是一种反模式。不要这样做。
我推荐这样的东西:
public static List<Book> getListWithPrefixInName(List<Book> books) {
return books.stream()
.map(b -> Book.of(b.getId(), PREFIX + ": " + b.getName()))
.collect(toList());
}
我有一个方法 (List<Book> getListWithPrefixInName(List<Book> books)
) 接受 collection 作为输入并对每个 collection object 执行转换。而且我不希望这些变化反映在转移collection上,只是为了return修改collection。因为我需要我原来的collection.
因此,我想通过创建一个新的 collection:
来复制List<Book> clone = new ArrayList<>(books);
可是我怎么错了... 我的代码:
public class App {
public static final String PREFIX = "PREFIX";
public static void main(String[] args) {
List<Book> books = new ArrayList<>();
books.add(Book.of(1L, "The Catcher in the Rye"));
books.add(Book.of(2L, "The Green Mile"));
List<Book> booksWithPrefix = getListWithPrefixInName(books);
for (Book book : books) {
if (book.getName().contains(PREFIX)) {
System.out.println(String.format("original book: '%s' have been changed", book));
}
}
}
public static List<Book> getListWithPrefixInName(List<Book> books) {
List<Book> clone = new ArrayList<>(books); // I thought that this cloning would be enough
return clone.stream()
.peek(b -> b.setName(PREFIX + ": " + b.getName()))
.collect(toList());
}
}
class Book {
private Long id;
private String name;
private Book(Long id, String name) {
this.id = id;
this.name = name;
}
public static Book of(Long id, String name) {
return new Book(id, name);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Book{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(id, book.id) &&
Objects.equals(name, book.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
因此我的整个原collection都被改变了,太糟糕了。
所以我不得不通过 third-party 库使用深度克隆:
import org.apache.commons.lang3.SerializationUtils;
public class App {
public static final String PREFIX = "PREFIX";
public static void main(String[] args) {
List<Book> books = new ArrayList<>();
books.add(Book.of(1L, "The Catcher in the Rye"));
books.add(Book.of(2L, "The Green Mile"));
List<Book> booksWithPrefix = getListWithPrefixInName(books);
for (Book book : books) {
if (book.getName().contains(PREFIX)) {
System.out.println(String.format("original book: '%s' have been changed", book));
}
}
}
public static List<Book> getListWithPrefixInName(List<Book> books) {
return books.stream()
.map(b -> SerializationUtils.clone(b)) // deep clone
.peek(b -> b.setName(PREFIX + ": " + b.getName()))
.collect(toList());
}
}
class Book implements Serializable {
private Long id;
private String name;
private Book(Long id, String name) {
this.id = id;
this.name = name;
}
public static Book of(Long id, String name) {
return new Book(id, name);
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Book{");
sb.append("id=").append(id);
sb.append(", name='").append(name).append('\'');
sb.append('}');
return sb.toString();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Book book = (Book) o;
return Objects.equals(id, book.id) &&
Objects.equals(name, book.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
这个问题有没有更优雅的解决方案?还是使用深度克隆是唯一正确的解决方案?
在流中更改状态(就像您在 peek
中所做的那样)是一种反模式。不要这样做。
我推荐这样的东西:
public static List<Book> getListWithPrefixInName(List<Book> books) {
return books.stream()
.map(b -> Book.of(b.getId(), PREFIX + ": " + b.getName()))
.collect(toList());
}