java - 如何在 Java 中正确复制带有对象的集合

标签 java collections java-8 clone

我有一个方法 ( List<Book> getListWithPrefixInName(List<Book> books) ),它接受一个集合作为输入并对每个集合对象执行转换。而我不希望这些变化反射(reflect)在转移的集合上,只是为了返回修改后的集合。因为我需要我的原始 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 都被更改了,这太糟糕了。

所以我不得不通过第三方库使用深度克隆:

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

关于java - 如何在 Java 中正确复制带有对象的集合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57020971/

相关文章:

java - 有没有办法使用 java8 进一步优化下面的代码?

java - 使用完整的 Java 路径从命令提示符执行可运行的 jar

javascript - 删除集合中不匹配的元素

java - 我们应该使用 HashSet 吗?

java - 如何在 Map java 中允许重复值?

java - isAfter isBefore Java 8 LocalDateTime 一日不同

java - 如何将 Mpesa Api 与 android 集成

java - 映射、分组、值列表

java - 枚举可以是非静态的吗?

java - 如何编写 jql 查询,该查询将在问题创建、解决、打开或关闭时获取数据?