gpt4 book ai didi

java - 使用 TreeMap 的流返回不连贯的结果

转载 作者:行者123 更新时间:2023-12-02 03:06:40 25 4
gpt4 key购买 nike

我正在尝试解决 Cay Horstmann 的“Core Java for the Impressive”中的以下练习:

When an encoder of a Charset with partial Unicode coverage can’t encode a character, it replaces it with a default—usually, but not always, the encoding of "?". Find all replacements of all available character sets that support encoding. Use the newEncoder method to get an encoder, and call its replacement method to get the replacement. For each unique result, report the canonical names of the charsets that use it.

为了教育目的,我决定使用流 API 来处理这个练习,尽管在我看来,更干净的解决方案会将计算分为多个步骤,中间变量为-之间(当然这会简化调试)。话不多说,这是我创建的代码怪物:

   Charset.availableCharsets().values().stream().filter(charset -> charset.canEncode()).collect(
Collectors.groupingBy(
charset -> charset.newEncoder().replacement(),
() -> new TreeMap<>((arr1, arr2) -> Arrays.equals(arr1, arr2) == true ? 0 : Integer.compare(arr1.hashCode(), arr2.hashCode())),
Collectors.mapping( charset -> charset.name(), Collectors.toList()))).
values().stream().map(list -> list.stream().collect(Collectors.joining(", "))).forEach(System.out::println);

基本上,我们只考虑canEncode的字符集;创建一个以 replacement 作为键、以规范名称列表作为值的 Map;因为分组对于默认实现 groupingBy(使用 HashMap)的数组不起作用,所以我决定使用 TreeMap。然后,我们使用规范名称的列表,用逗号将它们连接起来并打印。

不幸的是,我发现它给出的结果不连贯。如果我在同一个程序中运行该函数两次,第一个实例将返回由 23 个字符串组成的结果,第二个实例将返回由 21 个字符串组成的结果。我怀疑这与 TreeMapComparator 实现不佳有关,其定义如下:

((arr1, arr2) -> Arrays.equals(arr1, arr2) == true ? 0 : Integer.compare(arr1.hashCode(), arr2.hashCode()))

如果这是原因,那么在这种情况下,正确的比较器应该是什么?除此之外,单行线还能有什么改进吗?

我也很好奇,像我写的代码这样复杂的结构在专业程序中是否遇到过?也许只有我觉得看不懂?

最佳答案

不保证两个不同实例的哈希码会不同。这将是一个理想的情况,但不能保证。只有相反的情况才是正确的:如果两个对象相等,则它们具有相同的哈希码。

因此,如果您创建一个比较器,当对象具有相同的哈希码时,该比较器将对象视为相同,则任意对象都可能被视为相同。自 byte[] replacement() 返回的数组是防御性副本,读取临时对象,每次运行此代码时结果可能会有所不同。

此外,由于数组的哈希码与其内容无关,因此您的比较器违反了传递性规则:具有相同内容的两个数组应该是相同的,但因为它们可能/很可能具有不同的哈希码,与第三个数组比较时,它们具有不同的关系,不具有相同的内容,a == b ,但是a < cb > c 。这就是为什么甚至相等的数组的原因,您可以通过 Arrays.equals 进行比较可能最终属于不同的组,如 TreeSet随后与其他 key 进行比较时未能找到现有 key 。

如果你想按值比较数组,可以使用:

Charset.availableCharsets().values().stream().filter(Charset::canEncode).collect(
Collectors.groupingBy(
charset -> charset.newEncoder().replacement(),
() -> new TreeMap<>(Comparator.comparing(ByteBuffer::wrap)),
Collectors.mapping(Charset::name, Collectors.joining(", "))))
.values().forEach(System.out::println);

ByteBufferComparable并一致地评估包装数组的内容。

我移动了Collectors.joining收集器进入grouping避免创建临时收集器List无论如何,您稍后都会加入其内容。

顺便说一句,永远不要使用像 expression == true 这样的代码。没有理由附加 == trueexpression已经足够了。

<小时/>

由于您只对值感兴趣,换句话说,不需要键是某种类型,因此您可以预先包装所有数组,简化操作,甚至稍微提高效率:

Charset.availableCharsets().values().stream().filter(Charset::canEncode).collect(
Collectors.groupingBy(
charset -> ByteBuffer.wrap(charset.newEncoder().replacement()),
TreeMap::new,
Collectors.mapping(Charset::name, Collectors.joining(", "))))
.values().forEach(System.out::println);

如果不需要一致的迭代顺序,此更改甚至允许诉诸哈希:

Charset.availableCharsets().values().stream().filter(Charset::canEncode).collect(
Collectors.groupingBy(
charset -> ByteBuffer.wrap(charset.newEncoder().replacement()),
Collectors.mapping(Charset::name, Collectors.joining(", "))))
.values().forEach(System.out::println);

这有效,因为 ByteBuffer还实现 equalshashCode .

关于java - 使用 TreeMap 的流返回不连贯的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41631894/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com