1

我几乎没有什么问题/疑问可以在 HashMap 中填充值

我希望 HashMap 接受“Student”作为键,“Details”作为值。由于 hashMap 的密钥应该是不可变的,我有一些疑问,如果

  1. 学生类不可克隆
  2. 学生类有参考,而后者又参考了“实验室”

    public class Student {   
    
        private String id;
        private String name;
        private Department dept;
    
        public Student(String id, String name, Department dept)
        {
          this.id=id;
          this.name=name;
          this.dept=dept;
        }        
    
        public Department getDepartment()
        {
            return this.dept;
        }
    
    }
    
    public class Department {  
    
        private String deptId;
        private Lab lab;
    
        public Department(String deptId, Lab lab)
        {
            this.deptId=deptId;
            this.lab=lab;
        }
    
        public void setLab(Lab lab)
        {
            this.lab=lab;
        }
    }
    
    public class Lab {
    
        private String labId;
        private String labName;
    
        public Lab(String labId, String labName)
        {
            this.labId=labId;
            this.labName=labName;
        }
    
    }
    
    public class StudentDetails
    {
        private String fatherName;
        private String address
    
        public StudentDetails(String fatherName, String address)
        {
        this.fatherName=fatherName;
        this.address=address;
        }
    }
    
    
    public class StudentMaintainer {
    
        public static void main(String[] args)
        {
            StudentDetails stDetails= new StudentDetails("John","Mumbai");
            Lab lab= new Lab("100","CS");
            Department dept= new Department("900", lab);
            Student st = new Student("3000",dept);
    
            Map<Student,StudentDetails> studentMaintainer= new ArrayList<>();
            studentMaintainer.put(st,stDetails);
        }
    }
    

现在即使 Student 是可克隆的,我也可以获得 Department 的引用并调用 setLab() 来更改 StudentObject。(我错了吗?)

现在,如果 Department 和 Lab 来自 3rd 方 jar,如果 Student hashCode 是 (primeNumber+Student.id+Department.id+Lab.id).hashcode() [只是一些奇怪的情况],我如何在我的地图中使用 Student 对象;

4

3 回答 3

5

据我了解,不可变性与 Cloneable 无关,实际上恰恰相反。不可变性更多地与声明类 final 和使用不可变字段、不可覆盖的方法、没有 setter 方法、返回字段或不可变字段的深层副本的 getter 方法等有关......请阅读定义不可变对象的策略了解更多信息对此。

您的代码也有一个构造函数:

public void Student(String id, String name, Department dept)
{
  this.id=id;
  this.name=name;
  this.dept=dept;
}

一个真正的构造函数不应该被声明为返回任何东西,甚至不应该是 void。更好的是:

// note the difference?
public Student(String id, String name, Department dept)
{
  this.id=id;
  this.name=name;
  this.dept=dept;
}

此外,如果要作为 HashMap 的键正常工作,您的 Student 类应该正确覆盖 equals 和 hashCode。

于 2013-11-28T18:35:25.653 回答
4

现在即使 Student 是可克隆的,我也可以获得 Department 的引用并调用 setLab() 来更改 StudentObject。(我错了吗?)

你是对的。这可能会发生,并且可能导致您的 Student 类出现变异。对于Student不可变的实例,您不能修改其任何字段 [0]。这包括在其中一个字段上调用类似 setter 方法的东西。

现在,如果 Department 和 Lab 来自 3rd 方 jars,如果 Student hashCode 是 (primeNumber+Student.id+Department.id+Lab.id).hashcode() [只是一些奇怪的情况],我如何在我的 Map 中使用 Student 对象;

这是一个很好的问题。您显然不能仅仅将类更改为不可变的,因为您无法控制它们,因此您可能需要有点创意。可能的解决方案:

  • 如果您要使用的第三方对象是接口,您可以使用自己的类型实现接口,其中每个 mutator 方法的主体都会引发异常(例如java.util.Collections.unmodfiableList)。这样做的好处是您仍然可以在代码库中引用第三方类,但缺点是调用 mutator 方法将在运行时失败,而不是在编译时失败。
  • 在您自己的代码库中编写适配器,如下所示:

    public final class MyImmutableDepartment {
        private final MyImmutableLab lab;
        private final String departmentId;
    
        public MyImmutableDepartment(Department thirdPartyMutableDepartment) {
            this.departmentId = thirdPartyMutableDepartment.getId();
            this.lab = new MyImmutableLab(thirdPartyMutableDepartment.getLab());
        }
    
        // getters and the MyImmutableLab class left as an exercise
    }
    

    这样做的好处是您在编译时就知道,类不能被改变。

这两种方法的缺点是您基本上必须从第三方库中镜像每个类,以确保它们是不可变的。

我认为没有其他选择。

[0] 在某些情况下这是可能的,并且可以用于内部缓存,但这是学习时要坚持的一个不错的指导方针。

于 2013-11-28T20:30:11.737 回答
0

学生不需要是一成不变的!具体来说,要求是当键在 HashMap 中时,equals/hashCode 的行为不会改变。

这可以通过三种方式实现:

  • 不要实现equals/hashCode。如果您使用默认的引用相等,那么您如何改变密钥并不重要。为了澄清意图,重写这些方法,显式调用 super.equals,并使它们成为最终的。
  • 不要包含任何会在计算 equals/hashCode 时发生变化的字段。或者,如果字段的属性可能会更改但引用不会更改,请使用引用相等 (==) 而不是 field.equals() 并调用 System.identityHashCode(field) 而不是 field.hashCode()
  • 不要在对象用作 HashMap 中的键时对其进行变异。有点危险,但如果引用不是由您无法控制的代码持有,则可以正常工作。记录要求。

但是,在您的具体示例中,每个学生都有一个 ID。为什么在实现 equals/hashCode. 时要使用任何其他属性?

于 2013-12-28T11:17:50.623 回答