.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,对象结构正确。
- 新的数组列表"item"只是为.forEach测试提供一个简单的容器
- .forEach是用一段代码测试并设置断点。没用。
- .sort 旨在对同一容器中的列表重新排序。我没有使用流,因为我不想移动它。没用
- for 循环是为了证明列表具有有效数据以及 .forEach 的意图应该起作用的旧时尚方式。它确实按预期工作
我假设我的环境或这段代码有问题,但我无法自行识别。
在使用 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>
或者如果 RegistrationItem
的 getId()
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 添加了几个新的默认方法到Collection
、Iterable
和List
接口,包括:
forEach
parallelStream
removeIf
replaceAll
sort
spliterator
stream
通常,添加默认方法是安全的,因为它们必须根据其他现有方法来实现。但是,出于效率原因,实现 classes 以覆盖默认方法通常是个好主意。 Java 8 Vector
实现添加了对这些默认方法的多个覆盖。如果你有一个实际的 Vector
class 的实例,这些工作正常。 IndirectList
class 不会覆盖这些方法,因此它尝试设置的委托路径不适用于这些方法。相反,使用普通的 Vector
实现。不幸的是,IndirectList
方法不会使 superclass 状态保持最新,因此这些方法的 Vector
实现都表现得好像 Vector
是空的。
Vector
覆盖 forEach
、removeIf
、replaceAll
、sort
和 spliterator
。 parallelStream
和 stream
默认方法是根据 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超过继承。
我正在使用 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,对象结构正确。
- 新的数组列表"item"只是为.forEach测试提供一个简单的容器
- .forEach是用一段代码测试并设置断点。没用。
- .sort 旨在对同一容器中的列表重新排序。我没有使用流,因为我不想移动它。没用
- for 循环是为了证明列表具有有效数据以及 .forEach 的意图应该起作用的旧时尚方式。它确实按预期工作
我假设我的环境或这段代码有问题,但我无法自行识别。
在使用 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>
或者如果 RegistrationItem
的 getId()
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 添加了几个新的默认方法到Collection
、Iterable
和List
接口,包括:
forEach
parallelStream
removeIf
replaceAll
sort
spliterator
stream
通常,添加默认方法是安全的,因为它们必须根据其他现有方法来实现。但是,出于效率原因,实现 classes 以覆盖默认方法通常是个好主意。 Java 8 Vector
实现添加了对这些默认方法的多个覆盖。如果你有一个实际的 Vector
class 的实例,这些工作正常。 IndirectList
class 不会覆盖这些方法,因此它尝试设置的委托路径不适用于这些方法。相反,使用普通的 Vector
实现。不幸的是,IndirectList
方法不会使 superclass 状态保持最新,因此这些方法的 Vector
实现都表现得好像 Vector
是空的。
Vector
覆盖 forEach
、removeIf
、replaceAll
、sort
和 spliterator
。 parallelStream
和 stream
默认方法是根据 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超过继承。