.forEach 和 .sort 不起作用,不能在块中设置断点

.forEach and .sort don't work and cannot set breakpoints in blocks

我正在使用 Java 8(内部版本 1.8.0_25)、Netbeans 8.0.2 并将 Java 8 的一些功能整合到现有应用程序中。排序和 .forEach 不起作用,所以我创建了一些测试代码以确保我理解 lambda 等并诊断问题。下面是新代码以及与我系统中的数据进行交互的代码的组合:

  public void test(Registration reg) {
/* new code */
    List<String> family = new ArrayList<>();
    family.add("Mom");
    family.add("Dad");
    family.add("Brother");
    family.add("Sister");

    family.forEach(p -> System.out.println(p));

    Collections.sort(family, (p1,p2) -> {
        System.out.println(p1 + " <==> "+ p2);
        return p1.compareToIgnoreCase(p2);
            });

    family.forEach(p -> System.out.println(p));

/* code to test with my system data */
    List<RegistrationItem> item = new ArrayList<>();
    List<RegistrationItem> regI = reg.getRegistrationItem();

    regI.forEach(p -> {
        System.out.println(p.toString());
        item.add(p);
            });

    Collections.sort(regI, (r1,r2) -> {
        System.out.println(r1.toString() + r2.toString());
        return r1.getId().compareTo(r2.getId());
    });

    for (RegistrationItem r : regI) {
            item.add(r);
    }
}

Registration 是一个反映事件数据的 POJO,它包括一个 RegistrationItem(s) 列表,这是另一个 POJO 的详细信息。在此测试中,列表大小为 4。

标有新代码的部分运行良好。它打印出列表,在排序时打印,然后打印排序后的列表。我还可以在我期望的那种块内设置断点。

使用现有代码是另一回事。 .forEach 和 .sort 不起作用,我无法在 java 8 块中设置断点。调试器步进代码,但它似乎没有执行。当我进入 for 循环时,"item" 的大小仍然为 0。外观只是证明可以移动数据,这按预期工作并导致大小为 4。

如有任何帮助,我们将不胜感激。


抱歉我可能没说清楚。这只是测试代码,展示了自从更改为 java 8 后我在许多地方遇到的问题。比较器已更改为 lambda,for 循环到 .forEach 和 none 正在工作。此代码仅用于此发布。

在示例中,我已经验证了 reg 已正确传递给测试方法。它的结构正确,regI 的大小为 4,对象结构正确。

我假设我的环境或这段代码有问题,但我无法自行识别。

在使用 Holger 和 Stuart Marks 建议的方法进行测试后,很明显这与 IndirectList 和覆盖相关的问题相同。我的 JRE 和 JDK 都是 Java 8,我已经升级到 EclipseLink 2.5.2。我已经证明问题在使用比较器时 100% 发生,Collections.sort 使用 lambdas 和 .forEach。这似乎是一个非常普遍的问题,令我感到惊讶的是,除我之外,另一个问题没有引起比 1 个上升更多的关注。

尝试改变处理列表的方式。如果你想获取列表的项目,对它们进行排序,并将它们分配给一个新列表,那么试试这个:

List<RegistrationItem> sortedItems = reg.getRegistrationItem().stream()
    .sorted((a, b)-> a.getId().compareTo(b.getId()))
    .peek(System.out::println)
    .collect(Collectors.toList());

用forEach()然后相加,再排序,效率真的很低。使用流 API 可以让您以更加并行友好的方式完成所有这些工作,代码更少。

此代码中唯一可能出错的地方是 reg.getRegistrationItem() 不会 return 一个 List<RegistrationItem> 或者如果 RegistrationItemgetId() returns null.

您可以通过将空安全比较器分层到比较器来使比较器更加空安全。例如,以下代码将 null 视为比任何其他值都低的值:

    List<RegistrationItem> item = regI.stream()
        .sorted(Comparator.nullsFirst(
            Comparator.comparing(RegistrationItem::getId,
                Comparator.nullsFirst(String::compareTo))))
        .peek(System.out::println)
        .collect(Collectors.toList());

此问题的根本原因是在 EclipseLink JPA 的 IndirectList class 中使用了有缺陷的实现模式。 (doc, source) 这个问题出现在 2.5 版本系列中;它也可能出现在其他版本中。

问题是这个classsubclassesVector都有一个Vector 实例的引用。它试图通过覆盖 Vector 的所有方法来将所有方法调用委托给该实例。只要没有向 Vector.

添加新方法,这就可以正常工作

这发生在 Java 8.

Java 8 添加了几个新的默认方法CollectionIterableList接口,包括:

  • forEach
  • parallelStream
  • removeIf
  • replaceAll
  • sort
  • spliterator
  • stream

通常,添加默认方法是安全的,因为它们必须根据其他现有方法来实现。但是,出于效率原因,实现 classes 以覆盖默认方法通常是个好主意。 Java 8 Vector 实现添加了对这些默认方法的多个覆盖。如果你有一个实际的 Vector class 的实例,这些工作正常。 IndirectList class 不会覆盖这些方法,因此它尝试设置的委托路径不适用于这些方法。相反,使用普通的 Vector 实现。不幸的是,IndirectList 方法不会使 superclass 状态保持最新,因此这些方法的 Vector 实现都表现得好像 Vector 是空的。

Vector 覆盖 forEachremoveIfreplaceAllsortspliteratorparallelStreamstream 默认方法是根据 spliterator 实现的,所以同样的事情发生在它们身上。本质上,如果集合的 none 新默认方法用于从 EclipseLink JPA 检索的 IndirectList 实现,它们将起作用。

请注意,Collections.sort(indirectList) 也会出现此问题。这个方法只是调用了indirectList.sort()方法,所以遇到的问题和上面描述的完全一样。

this answer.

中列出了此问题的一些潜在解决方法

有关 EclipseLink 状态的更多信息,请参阅 EclipseLink JPA 错误 433075 and 446236

有关此实施模式的缺陷的更多信息,请参阅 Joshua Bloch 的书 Effective Java,第二版 Item 16:Favor Composition超过继承。