Cómo copiar correctamente colecciones con objetos en Java

FreeOnGoo:

Tengo un método ( List<Book> getListWithPrefixInName(List<Book> books)) que acepta una colección como entrada y realiza una transformación con cada objeto de colección. Y no quiero que estos cambios se reflejen en la recopilación transferido, sólo para devolver la colección modificada. Porque necesito mi colección original.

De acuerdo con ello, pensé que copiar simplemente mediante la creación de una nueva colección:

List<Book> clone = new ArrayList<>(books);

Pero, ¿cómo me equivoco ... Mi código:

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

Y en consecuencia toda mi colección original ha sido cambiado, es terrible.

Así que tuve que usar la clonación de profundidad a través de una biblioteca de terceros:

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

¿Hay una solución más elegante a este problema? O es el uso de la clonación profunda la única solución correcta?

Miguel :

Cambio de estado dentro de una corriente (como lo están haciendo en peek) es un patrón contra. No haga esto.

Yo recomiendo algo como esto:

  public static List<Book> getListWithPrefixInName(List<Book> books) {
        return books.stream()
                .map(b -> Book.of(b.getId(), PREFIX + ": " + b.getName()))
                .collect(toList());
    }

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=317449&siteId=1
Recomendado
Clasificación