在一行的 retainAll 之后将 size() 添加到 keySet()

Add size() to keySet() after retainAll on one line

我正在试验视图。是否有可能在不使用 lambda 的情况下在倒数第二行编写 size() 方法?如下:

eintraege.retainAll(Arrays.asList(names)).size(); // Does not work


public static int countNames(Map<String, PhoneNumber> phoneBook,
                             String[] names) {
    Set<String> eintraege = phoneBook.keySet();
    eintraege.retainAll(Arrays.asList(names));  
    return eintraege.size();  // Works fine
}

此代码无法正常工作。

retainAll 将删除所有不在 names 中的键。 .keySet() 没有 return 副本。 return 是另一种观点。 .keySet() returns 中删除元素将从原始地图中删除它们!

因此,此代码将给出正确的答案...并且还会损坏您的 phone书籍。

如果必须使用 retainAll,则必须复制一份。不,无法链接 size() 调用。

但是,复制在这里很愚蠢。你真的只是想,嗯,数名字。这段代码不会破坏原始的 phone 书,而且速度要快得多:

int count = 0;
for (String n : names) if (phoneBook.containsKey(n)) count++;
return count;

这也显示了 'worrying about lines' 的愚蠢。上面的代码是 3 行,并且可以按原样合理地阅读。当然,很多使用 lambda 的人会将完全相同的 'semantic load' 放在一行中。但这并不是特别常用的代码风格。在某些时候,您 'measuring the efficacy of your code style' 比实际测量代码的复杂性还多。您还会看到毫无用处并使代码更难阅读的极端情况:一个 .stream().map() 等操作执行 18 种不同的操作,全部堆积在一条巨大的线上,并且有些小丑在说:哈!看!更短!

牢记数据结构的特征。 list.contains(n) 需要的时间随着列表的大小而增加。 map.containsKey(n)set.contains(n),这需要常数时间(或者在 TreeMap 的情况下,这需要与 log2(n) 成比例的时间,但请记住 log2(1_000_000) 仍然是只有几十个循环。即使对于巨大的输入,计算机也可以进行对数缩放。

因此,上面的代码比较快,但是如果你反过来看:

for (String n : phoneBook.keySet()) if (names.contains(n)) count++;

这确实会变得非常缓慢,尤其是如果 names 很大。