From feed43222583ddef36f49469fa5ad27dcc824ea6 Mon Sep 17 00:00:00 2001 From: sjsdfg <736777445@qq.com> Date: Wed, 20 Mar 2019 14:11:27 +0800 Subject: [PATCH] =?UTF-8?q?54.=20=E8=BF=94=E5=9B=9E=E7=A9=BA=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E7=BB=84=E6=88=96=E9=9B=86=E5=90=88=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E8=A6=81=E8=BF=94=E5=9B=9E=20null.md?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../54. 返回空的数组或集合,不要返回 null.md | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 docs/notes/54. 返回空的数组或集合,不要返回 null.md diff --git a/docs/notes/54. 返回空的数组或集合,不要返回 null.md b/docs/notes/54. 返回空的数组或集合,不要返回 null.md new file mode 100644 index 0000000..d3547c0 --- /dev/null +++ b/docs/notes/54. 返回空的数组或集合,不要返回 null.md @@ -0,0 +1,75 @@ +# 54. 返回空的数组或集合,不要返回 null + +  像如下的方法并不罕见: + +```java +// Returns null to indicate an empty collection. Don't do this! +private final List cheesesInStock = ...; + +/** + * @return a list containing all of the cheeses in the shop, + * or null if no cheeses are available for purchase. + */ +public List getCheeses() { + return cheesesInStock.isEmpty() ? null + : new ArrayList<>(cheesesInStock); +} +``` + +  没有理由对没有奶酪 (Cheese) 可供购买的情况进行特殊处理。这样需要在客户端做额外的代码处理可能为 null 的返回值,例如: + +```java +List cheeses = shop.getCheeses(); +if (cheeses != null && cheeses.contains(Cheese.STILTON)) + System.out.println("Jolly good, just the thing."); +``` + +  在几乎每次使用返回 null 来代替空集合或数组的方法时,都需要使用这种迂回的方式。 它容易出错,因为编写客户端的程序员可能忘记编写特殊情况代码来处理 null 返回。 多年来这种错误可能会被忽视,因为这种方法通常会返回一个或多个对象。 此外,返回 null 代替空容器会使返回容器的方法的实现变得复杂。 + +  有时有人认为,null 返回值比空集合或数组更可取,因为它避免了分配空容器的开销。这个论点有两点是不成立的。首先,除非测量结果表明所讨论的分配是性能问题的真正原因,否则不宜担心此级别的性能(详见第 67 条)。第二,可以在不分配空集合和数组的情况下返回它们。下面是返回可能为空的集合的典型代码。通常,这就是你所需要的: + +```java +//The right way to return a possibly empty collection +public List getCheeses() { + return new ArrayList<>(cheesesInStock); +} +``` + +  如果有证据表明分配空集合会损害性能,可以通过重复返回相同的不可变空集合来避免分配,因为不可变对象可以自由共享 (条目 17)。下面的代码就是这样做的,使用了 `Collections.emptyList` 方法。如果你要返回一个 Set,可以使用 `Collections.emptySet` ;如果要返回 Map,则使用 `Collections.emptyMap`。但是请记住,这是一个优化,很少需要它。如果你认为你需要它,测量一下前后的性能表现,确保它确实有帮助: + +```java +// Optimization - avoids allocating empty collections +public List getCheeses() { + return cheesesInStock.isEmpty() ? Collections.emptyList() + : new ArrayList<>(cheesesInStock); +} +``` + +  数组的情况与集合的情况相同。 永远不要返回 null,而是返回长度为零的数组。 通常,应该只返回一个正确长度的数组,这个长度可能为零。 请注意,我们将一个长度为零的数组传递给 `toArray` 方法,以指示所需的返回类型,即 `Cheese []`: + +```java +//The right way to return a possibly empty array +public Cheese[] getCheeses() { + return cheesesInStock.toArray(new Cheese[0]); +} +``` + +  如果你认为分配零长度数组会损害性能,则可以重复返回相同的零长度数组,因为所有零长度数组都是不可变的: + +```java +// Optimization - avoids allocating empty arrays +private static final Cheese[] EMPTY_CHEESE_ARRAY = new Cheese[0]; + +public Cheese[] getCheeses() { + return cheesesInStock.toArray(EMPTY_CHEESE_ARRAY); +} +``` + +  在优化的版本中,我们将相同的空数组传递到每个 `toArray` 调用中,当 `cheesesInStock` 为空时,这个数组将从 `getCheeses` 返回。不要为了提高性能而预先分配传递给 `toArray` 的数组。研究表明,这样做会适得其反[Shipilev16]: + +```java +// Don’t do this - preallocating the array harms performance! +return cheesesInStock.toArray(new Cheese[cheesesInStock.size()]); +``` + +  总之,**永远不要返回 null 来代替空数组或集合**。它使你的 API 更难以使用,更容易出错,并且没有性能优势。 \ No newline at end of file