首选可变或不可变集合作为方法参数
Preferring mutable or immutable collections as method parameters
我正在编写一个将在多个地方使用的库方法。该方法的参数之一是对象集合,并且该方法不会改变该集合。方法签名应该指定可变集合还是不可变集合?
选项 1:可变集合作为参数
public static void foo(List<Bar> list) {
// ...
}
优点:客户可以传入 List<Bar>
或 ImmutableList<Bar>
中对他们来说更方便的一个。
缺点:list
参数不会发生变化这一点不是很明显。客户端必须阅读文档 and/or 代码才能实现这一点。无论如何,客户可能会制作不必要的防御性副本。
选项 2:不可变集合作为参数
public static void foo(ImmutableList<Bar> list) {
// ...
}
优点:客户可以保证 list
参数不会发生变化。
缺点:如果客户端有 List<Bar>
,他们必须先将其转换为 ImmutableList<Bar>
,然后再调用 foo
。这种转换会浪费少量时间,并且 强制 对客户不管他们喜欢与否。
注意:为了这个问题的目的,我们假设所有客户端都将有 Guava 的 ImmutableList
可用,例如因为库和客户端代码都属于已经使用 ImmutableList
别处。
作为 foo()
API 的创建者,这是您公司的 none。你拿了一个列表,你不修改它,就是这样。事实上,您的代码并不关心列表的可变性(这是调用者关心的问题):因此记录您的意图并停在那里。
如果调用者需要保证列表不会被篡改,他们会创建防御副本不是因为你不承诺保持列表不变,但是因为他们需要这种保证。
它遵循我们在方法实现中执行空检查的相同逻辑:它是必需的,因为我们的代码需要健壮,而不是因为调用者可以发送空参数。
换句话说,记录您打算实现的方法,并由调用者选择列表实现。他们选择的原因会有所不同(即,并不总是只是您是否会修改列表)。
留在 List
。
以下是一些需要考虑的情况:
Collections.synchronizedCollection
不修改客户端的集合。
- 如果它强制客户端输入不可变集合,那么将其设为同步集合就没有用了,因为不可变集合已经是线程安全的了。
Collections.frequency
不修改客户端的集合。
- 为了检查频率,用户将被迫忍受将元素转移到新集合的额外开销。
这些原因并不是 JDK 不公开不可变接口的原因。解释了这些原因 in the documentation。在 synchronizedCollection
的情况下,虽然它不修改客户端的集合,但它 return 客户端集合的可修改视图;有人会说这个功能在这里不适用。但是,frequency
和其他查询功能仍然很强大。
您不应该为了宣传安全而限制客户,仅此而已。应该有更多的理由,否则你试图提供帮助可能会给客户带来负担。在某些情况下,您的目标可能与 function/system 实现的目标相矛盾,例如 synchronizedCollection
示例。
鼓励安全是件好事,但是让您的系统将安全强加给您的客户而不让系统从中受益将是滥用权力;这不是你的决定。
ernest_k提出了一个很好的观点,提示你需要分析一下你的系统应该负责什么,客户端应该负责什么。在这种情况下,集合是否不可变应该由客户决定,因为您的系统不关心可变性。正如他所说,“这是 none 你的事”,我同意这一点。
我正在编写一个将在多个地方使用的库方法。该方法的参数之一是对象集合,并且该方法不会改变该集合。方法签名应该指定可变集合还是不可变集合?
选项 1:可变集合作为参数
public static void foo(List<Bar> list) {
// ...
}
优点:客户可以传入 List<Bar>
或 ImmutableList<Bar>
中对他们来说更方便的一个。
缺点:list
参数不会发生变化这一点不是很明显。客户端必须阅读文档 and/or 代码才能实现这一点。无论如何,客户可能会制作不必要的防御性副本。
选项 2:不可变集合作为参数
public static void foo(ImmutableList<Bar> list) {
// ...
}
优点:客户可以保证 list
参数不会发生变化。
缺点:如果客户端有 List<Bar>
,他们必须先将其转换为 ImmutableList<Bar>
,然后再调用 foo
。这种转换会浪费少量时间,并且 强制 对客户不管他们喜欢与否。
注意:为了这个问题的目的,我们假设所有客户端都将有 Guava 的 ImmutableList
可用,例如因为库和客户端代码都属于已经使用 ImmutableList
别处。
作为 foo()
API 的创建者,这是您公司的 none。你拿了一个列表,你不修改它,就是这样。事实上,您的代码并不关心列表的可变性(这是调用者关心的问题):因此记录您的意图并停在那里。
如果调用者需要保证列表不会被篡改,他们会创建防御副本不是因为你不承诺保持列表不变,但是因为他们需要这种保证。
它遵循我们在方法实现中执行空检查的相同逻辑:它是必需的,因为我们的代码需要健壮,而不是因为调用者可以发送空参数。
换句话说,记录您打算实现的方法,并由调用者选择列表实现。他们选择的原因会有所不同(即,并不总是只是您是否会修改列表)。
留在 List
。
以下是一些需要考虑的情况:
Collections.synchronizedCollection
不修改客户端的集合。- 如果它强制客户端输入不可变集合,那么将其设为同步集合就没有用了,因为不可变集合已经是线程安全的了。
Collections.frequency
不修改客户端的集合。- 为了检查频率,用户将被迫忍受将元素转移到新集合的额外开销。
这些原因并不是 JDK 不公开不可变接口的原因。解释了这些原因 in the documentation。在 synchronizedCollection
的情况下,虽然它不修改客户端的集合,但它 return 客户端集合的可修改视图;有人会说这个功能在这里不适用。但是,frequency
和其他查询功能仍然很强大。
您不应该为了宣传安全而限制客户,仅此而已。应该有更多的理由,否则你试图提供帮助可能会给客户带来负担。在某些情况下,您的目标可能与 function/system 实现的目标相矛盾,例如 synchronizedCollection
示例。
鼓励安全是件好事,但是让您的系统将安全强加给您的客户而不让系统从中受益将是滥用权力;这不是你的决定。
ernest_k提出了一个很好的观点