我想创建一个不可变记录,它有 2 个可变字段 Date 和一个 HashMap
public record ImmutableRecord(String name, LocalDate admissionDate, Date dateOfBirth, Map<String, Integer> metaData) {
public ImmutableRecord{
// Date is a mutable field
dateOfBirth = new Date(dateOfBirth.getTime());
// HashMap is a mutable field
Map<String, Integer> tempMap = new HashMap<>();
for(Map.Entry<String, Integer> entry: metaData.entrySet()){
tempMap.put(entry.getKey(), entry.getValue());
}
metaData = tempMap;
//Can I use following instead of above for deep copying the map?
metaData = Map.copyOf(metaData);
}
}
以下哪种方法是正确的,使用 forEach 深度克隆每个字段或使用 Map.copyOf
首先,从设计上来说,通常复制数据的责任应该落在实例化记录的调用方法上。通常,构造函数不应该进行此复制。 让我们更改该记录的名称,因为“ImmutableRecord”是多余的,并且无法反映您的问题域。考虑到入学日期字段,显然您打算代表学生。所以你的整个类定义可能是:
public record Student ( String name, LocalDate admitted, LocalDate dateOfBirth, Map<String, Integer> metaData ) {}
应始终使用现代且不可变的
LocalDate
类,而不是可变的遗留类
java.sql.Date
类。调用方法应该进行复制,如下所示。
关于您尝试克隆地图:
Map<String, Integer> tempMap = new HashMap<>();
for(Map.Entry<String, Integer> entry: metaData.entrySet()){
tempMap.put(entry.getKey(), entry.getValue());
}
这种复制不是
深度克隆。您没有复制密钥的内容。您也没有复制值的内容。您确实创建了另一个 Map
对象。但旧映射和新映射都有包含相同键和值对象的
引用的条目。 您的复制实际上与仅将旧地图传递给新地图的构造函数相同。新旧映射都有指向相同键对象和相同值对象的条目。
Map < String , Integer > metaData = new HashMap <> ( oldMetaData ) ;
您的复制几乎与
Map.copyOf( oldMap )
相同,只是
copyOf
生成某个实现 Map
的未指定类的不可修改映射,而不是可修改的 HashMap
。但就像上面的代码一样,新旧映射都指向相同的键对象和相同的值对象。所以调用代码看起来像这样:
Student someStudent = new Student( "Alice" , … ) ;
…
Student twinStudent =
new Student(
"Bob" ,
LocalDate.of( 2021, Month.MARCH, 23 ),
someStudent.dateOfBirth,
Map.copyOf( someStudent.metaData )
)
;
记录功能的目的是简洁地定义一个类,其主要目的是透明且不可变地通信数据。因此理想情况下,记录应该包含不可变的内容。这是更喜欢
Map.copyOf
而不是新 HashMap
的另一个原因。
正如评论,更喜欢
Map.copyOf
的另一个原因是,如果传递的映射已经不可修改,则直接返回传递的映射。使用的内存更少,CPU 的使用也更少。
正如注释的,使用
Map.copyOf
的一个可能的限制是,在地图引用、任何键或任何值中,不能
容忍空值。
Map.copyOf
是“无空区”,我自己新创造的术语。
如果您的方案中可以容忍 null,则替代方案是
Collections.unmodifiableMap
Collections.unmodifiableMap
包裹的地图。
metaData = Collections.unmodifiableMap( new HashMap<>( oldMetaData ) ) ;
最好将设计决策视为二元的……即它是否是不可变的?@Data
此外您还可以:
添加@Builder为您提供流畅的构建器