【问题标题】:add a defensive copy of an object to a hashset将对象的防御性副本添加到哈希集
【发布时间】:2017-05-24 14:29:13
【问题描述】:

目前我有这个代码:

public final class Tutor {
private String name;
private final Set<Student> tutees;
public Tutor(String name, Student[] students){
    this.name = name;
     tutees = new HashSet<Student>();
     for (int i = 0; i<students.length; i++)
         tutees.add(students[i]);
}

我正在尝试重写它(只是在纸上),以便它制作/添加学生的防御性副本,而不是直接将它们添加到哈希集中,我想知道以下代码是否会这样做:

public final class Tutor {
private String name;
private final Set<Student> tutees;
public Tutor(String name, Student[] students){
    this.name = name;
     tutees = new HashSet<Student>();
     for (int i = 0; i<students.length; i++)
         tutees.add(students[i](students.getName(), students.getCourse());
}

学生代码(如果需要):

public class Student {
private String name;
private String course;
public Student(String name, String course){
     this.name = name;
     this.course = course;
}
public String getName() { return name; }
public String getCourse() { return course; }
public void setName(String name) {
     this.name = name;
}
public void setCourse(String course){
     this.course = course;
 }
}   

谢谢

【问题讨论】:

  • 首先使用编译器。它会发现至少一个错误。接下来,考虑为 Student 编写一个以 Student 作为参数的构造函数——封装在 Student 类中复制学生所需的内容。

标签: java hashset defensive-programming defensive-copy


【解决方案1】:

你做得对,但有一些错误,因为你是在纸上写的。如果你将它重写到程序中,它不会编译,由于这一行

tutees.add(students[i](students.getName(), students.getCourse());

需要替换为

tutees.add(new Student(students[i].getName(), students[i].getCourse());

注意,您正在添加新的 Student,但字段由 现有引用 初始化,这导致 浅复制 - 对象不同但分享内容。但是,String 类是 immutable,这意味着修改字符串的每个方法都会创建具有应用修改的新字符串,而旧字符串保持不变。所以即使原始学生和它的副本共享内容,字符串修改也不会相互影响,因此我们可以说它的行为类似于defensive-copy

Student original = new Student("name", "course");
Student copy = new Student(original.getName(), original.getCourse());
// does not change the name of the copy
String modifiedName = copy.getName().replaceAll("a", "b"); 

这是一个真正的防御性复制(深复制)示例:

Student deepCopy = new Student(
        new String(original.getName()), 
        new String(original.getCourse())
);

出于效率原因,如果您知道自己正在使用 immutable 的类,只需复制它们的引用即可。

【讨论】:

  • 感谢 matoni,我认为这足以解决问题!怀疑我在某个地方需要一个“新”,但无法理解它:)
【解决方案2】:

您已经发现将可变学生放入Set 是一个坏主意的问题。您不想在某个系列中更改某些内容,因为它违反了系列的合同。

创建副本可以解决症状,但不能解决根本问题。问题是您的 Student 类是可变的。如果您将 Student 类设置为不可变类,则无需担心复制问题,而且出错的可能性会大大降低。

public class Student {
    private String name;
    private String course;
    public Student(String name, String course){
        this.name = name;
        this.course = course;
    }
    public String getName() { return name; }
    public String getCourse() { return course; }
}

如果学生改名 - 这种情况多久发生一次?在您的系统中,您可能根本不需要对其建模 - 或更改课程,您只需创建一个新学生并删除旧的不正确的学生。

【讨论】:

  • 谢谢迈克尔!我知道学生的可变性是一个潜在的问题,只是这个特定的练习要求学生不被改变。当然,在现实生活场景中解决这个问题要容易得多
猜你喜欢
  • 2011-02-26
  • 2011-01-15
  • 1970-01-01
  • 1970-01-01
  • 2019-04-09
  • 1970-01-01
  • 1970-01-01
  • 2018-06-14
  • 1970-01-01
相关资源
最近更新 更多