.forEach 和 .sort 不起作用,并且无法在块中设置断点

2022-01-22 00:00:00 collections java-8 java jpa java-stream

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

I am using Java 8 (build 1.8.0_25), Netbeans 8.0.2 and am incorporating some of the Java 8 features into an existing app. Sorting and .forEach is not working so I have created some test code to ensure I understand lambdas, etc. and to diagnose the problem. Below is a mix of new code as well as code to interact with the data from my system:

  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.

Registration is a POJO reflecting data for an event which includes a List of RegistrationItem(s) which is another POJO of details. In this test the list size is 4.

标记为新代码的部分完美运行.它打印出列表,在排序时打印,然后它们打印排序后的列表.我还可以在我所期望的那种类型的块内设置断点.

The section labelled new code works perfectly. It prints out the list, prints as it sorts and them prints the sorted list. I can also set breakpoints inside the block of the sort which is what I would expect.

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

The working with existing code is another matter. The .forEach and .sort don't work and I can not set breakpoints in the java 8 blocks. The debugger steps to the code but it doesn't seem like it executes. When I get to the for loop, "item" still has a size of 0. The look is just there to prove that the data can be moved which works as expected and results in a size of 4.

任何帮助将不胜感激.

抱歉,我可能不清楚.这只是测试代码,演示了自从更改为 java 8 以来我在几十个地方遇到的问题.比较器更改为 lambda,for 循环更改为 .forEach,但没有一个工作正常.此代码仅用于此发布.

I apologize I probably wasn't clear. This is just test code demonstrating a problem I am experiencing in dozens of places since changing to java 8. comparators were changed to lambdas and for loops to .forEach and none are working. This code has no purpose other than for this posting.

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

In the example, I have verified that reg is passed correctly to the test method. It is structured correctly and regI has a size of 4 with correctly structured objects.

  • 新的数组列表item"只是为.forEach测试提供一个简单的容器
  • .forEach 是用一段代码进行测试并设置断点.它不起作用.
  • .sort 旨在对同一容器中的列表重新排序.我没有使用流,因为我不想移动它.它不起作用
  • for 循环是为了证明列表具有有效数据以及 .forEach 的意图应该有效的老式方式.它确实按预期工作

我认为我的环境或此代码有问题,但我无法自行识别.

I assume I have something wrong in my environment or this code but have been unable to identify it on my own.

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

After testing with the approach suggested by Holger and Stuart Marks, it is pretty clear that this is the same problem related to IndirectList and overrides. My JRE and JDK are both Java 8 and I have upgraded to EclipseLink 2.5.2. I have proven the problem to occur 100% of the time with comparator, Collections.sort with lambdas and .forEach. This seems like a very pervasive problem and I am surprised the other question has not attracted more attention than 1 uptick besides mine.

推荐答案

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

The root cause of this problem is the use of a flawed implementation pattern in the IndirectList class of EclipseLink JPA. (doc, source) This problem occurs in the 2.5 release family; it may also occur in other versions.

问题在于这个类的子类 Vector 和都有一个 对Vector 实例的引用.它试图通过覆盖 Vector 的所有方法将所有方法调用委托给该实例.只要没有向 Vector 中添加新方法,就可以正常工作.

The problem is that this class both subclasses Vector and has a reference to a Vector instance. It attempts to delegate all method calls to that instance by overriding all of the methods of Vector. This works fine, as long as no new methods are added to Vector.

这发生在 Java 8 中.

This happened in Java 8.

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

Java 8 added several new default methods to the Collection, Iterable, and List interfaces, including:

  • forEach
  • 并行流
  • removeIf
  • 全部替换
  • 排序
  • 分隔符

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

Typically, adding default methods is safe, since they must be implemented in terms of other existing methods. However, it is usually a good idea for implementing classes to override default methods for reasons of efficiency. The Java 8 Vector implementation added several overrides of these default methods. These work fine if you have an instance of the actual Vector class. The IndirectList class doesn't override these methods, so the delegation path that it attempted to set up doesn't work for these methods. Instead, the ordinary Vector implementations are used. Unfortunately, the IndirectList methods don't keep the superclass state up to date, so the Vector implementations of the methods all behave as if the Vector is empty.

Vector 覆盖 forEachremoveIfreplaceAllsort 和 <代码>分离器.parallelStreamstream 默认方法是根据 spliterator 实现的,所以同样的事情发生在它们身上.本质上,如果在从 EclipseLink JPA 检索到的 IndirectList 实现中使用集合上的新默认方法,则它们都不会起作用.

Vector overrides forEach, removeIf, replaceAll, sort, and spliterator. The parallelStream and stream default methods are implemented in terms of spliterator, so the same thing happens to them. Essentially, none of the new default methods on collections will work if they are used on an IndirectList implementation retrieved from EclipseLink JPA.

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

Note that this problem also occurs with Collections.sort(indirectList). This method simply calls the indirectList.sort() method, so it encounters exactly the same problem as described above.

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

Some potential workarounds for this problem are listed in this answer.

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

For further information about the status of EclipseLink, see EclipseLink JPA bugs 433075 and 446236.

有关此实现模式的缺陷的更多信息,请参阅 Joshua Bloch 的书Effective Java, Second Edition,第 16 条:优先组合优于继承.

For further information about the pitfalls of this implementation pattern, see Joshua Bloch's book Effective Java, Second Edition, Item 16: Favor Composition over Inheritance.

相关文章