Java 8列表副本[重复]

Posted

技术标签:

【中文标题】Java 8列表副本[重复]【英文标题】:Java 8 List copy [duplicate] 【发布时间】:2018-05-29 16:59:52 【问题描述】:

我有一个员工对象

我无法更新员工对象

public class Employee 

    public Employee(Integer id, Integer age, String gender, String fName, String lName) 
        this.id = id;
        this.age = age;
        this.gender = gender;
        this.firstName = fName;
        this.lastName = lName;
    

    private Integer id;
    private Integer age;
    private String gender;
    private String firstName;
    private String lastName;

我最初设置了一个员工列表,但想创建该列表的副本,然后对其进行更改。即使我正在创建新列表,它仍然会更改原始列表

public class ListTest 

    public static void main(String[] args) 

        List<Employee> employeeList = new ArrayList<>();
        Employee employee = new Employee(1, 1, "MALE", "TEST", "TEST");
        employeeList.add(employee);

        List<Employee> listTwo = new ArrayList<>();
        listTwo.addAll(employeeList);

        listTwo.get(0).setAge(11);

        System.out.println(employeeList.get(0).getAge());

    

输出为 11。

有没有更简单的方法可以从原始列表中克隆,并且任何更改都不应该应用于原始列表对象,而只是我创建的新列表

【问题讨论】:

如何让它不可变并提供一个复制构造函数? 我已经编辑了这个问题。 Java有没有办法做到这一点? 有您的经验的用户可能听说过“做事先研究”... 您需要注意,在 Java 中 一切都是对象,除了基本类型之外,一切都是引用以节省内存。两个数组的第一个元素是同一个对象 @GhostCat 你知道这个问题有很好的答案吗?我没想到用户会对您的体验发表评论。 【参考方案1】:

由于您无法更改 Employee 类本身,因此您可以创建一个帮助方法来深度复制 Employee 实例(您当然也可以直接调用构造函数,但我找到了一个简单的方法清洁器):

public static Employee copy(Employee original)
    return new Employe(original.id, original.age, original.gender, original.fname, original.lname);

然后您可以使用映射操作流式传输列表并复制每个元素:

List<Employee> employeesCopy = employees.stream() // Stream<Employee>
    .map(e -> copy(e))                            // Stream<Employee>
    .collect(Collectors.toList());                // List<Employee>

【讨论】:

【参考方案2】:

它并没有改变原始列表,而是改变了两个列表中包含的对象。

在这种情况下,让员工对象不可修改并提供创建一个属性相对于原始对象已更改的副本对象的可能性可能非常有用。

我不确定clone() 是否会创建对象的深层副本。如果是,all is fine,如果不是,你可以use streams to create a deep copy。

如果你绝对不能修改Employee 对象(甚至不添加复制构造函数),你可以这样做:

public static Employee clone(Employee original) 
    return new Employee(original.getID(), original.getAge(), original.getGender(), original.getFirstName(), original.getLastName());

然后做

employeeList.stream().map(item -> clone(item)).collect(Collectors.toList());

或者只是

employeeList.stream().map(<thisclassname>::clone).collect(Collectors.toList());

(与 Uata 建议的基本相同,但您自己进行复制)。

【讨论】:

这只是一个示例,但我无法修改员工对象。解决办法是什么? @Makky 查看 Uata 的回答,但我会扩展我的。 @Makky 只要您有构造函数,您就可以创建 Employee 实例的新副本(假设字段是不可变的)。考虑这个示例代码: ` public static void main(String[] args) List list0 = Arrays.asList(new A(1), new A(2), new A(3)); List list1 = list0.stream().map(a -> new A(a.getVal())).collect(toList()); list1.forEach(a -> a.setVal(a.getVal() * 2)); 类 A 私有 int val; A(int val) this.val = val; int getVal() 返回值; void setVal(int val) this.val = val; ` 谢谢@glglgl 我会根据你的回答做出改变 获取它的另一种方法是调用构造函数而不是克隆方法,这样:employeeList.stream().map(::new).collect(Collectors.toList()) ;【参考方案3】:

首先在您的 Employee 类中创建一个复制构造函数:

    public Employee(Employee employee) 
        this.id = employee.id;
        this.age = employee.age;
        this.gender = employee.gender;
        this.firstName = employee.firstName;
        this.lastName = employee.lastName;
    

然后您可以使用 Java 8 流进行深度复制:

List<Employee> listTwo = employeeList.stream().map(item -> new Employee(item)).collect(Collectors.toList());

【讨论】:

以上是关于Java 8列表副本[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从列表的副本中删除时删除的列表元素[重复]

带有集合或列表的 Java 8 CompletableFuture.allOf(...) [重复]

Java 8 Stream排序字符串列表[重复]

使用 Java 8 在数组/列表中收集文件名 [重复]

Java 8 流:列表到按 [重复] 分组的平面地图

通过流将带有列表的列表对象转换为Java 8中的映射[重复]