ImmutableMap.Builder有上限吗?
Does ImmutableMap.Builder have a limit?
我有一个 class 看起来像这样:
public enum Animal {
BEAR,
SHEEP,
LION;
private static final Map<String, Animal> MAPPING = new ImmutableMap.Builder<String, Animal>()
.put("BLACK_BEAR", BEAR)
.put("WHITE_BEAR", BEAR)
...
.put("African Lion", LION)
.build()
}
大约 1500 个地图条目。
我尝试构建我的应用程序,但出现以下错误:
[javac] An annotation processor threw an uncaught exception.
[javac] Consult the following stack trace for details.
[javac] java.lang.WhosebugError
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
我尝试将地图的大小减小到 15 左右,但构建良好。我们可以对静态不可变映射进行硬编码的大小是否有限制?我没有在文档中看到任何限制。可能是什么问题?
不是ImmutableBuilder;事实上,你的源文件现在是一堆巨大的 AST 节点,lombok 的处理最终占用了太多内存,我猜,考虑到 javac 可以处理它,这在技术上是 lombok 中的一个错误。换句话说,编译器无法将您的 java 文件转换为 class 文件,但如果可以,它会工作得很好*。
我通常会将任何试图在源中定义那么多数据的源文件视为代码味道。
我建议你把这是什么数据放在一个文本文件中。
将此文本文件与您的源文件放在一起;如果你使用 maven / 或多或少的标准源代码目录结构,你会说:
src/main/java/com/foo/yourpkg/Animal.java
src/main/resources/com/foo/yourpkg/AnimalData.txt
这将自动导致 txt 文件与您的 class 文件位于同一位置,即使在 jars 中也是如此。然后,从您的动物文件中阅读它:
public enum Animal {
private static final Map<String, Animal> MAPPING;
static {
try {
Map<String, String> example = new HashMap<String, String>();
try (var in = Animal.class.getResourceAsStream("AnimalData.txt")) {
BufferedReader br = new BufferedReader(new InputStreamReader(
in, StandardCharsets.UTF_8));
for (String line = br.readLine(); line != null; line = br.readLine()) {
String[] p = line.split("\t", 2);
map.put(p[0], p[1]);
}
}
MAPPING = Collections.unmodifiableMap(map);
} catch (IOException e) {
throw new InternalError("data corrupted");
}
}
}
这个假设的例子需要您在文本文件中放入 tab-separated 个字符串对。您可以使用此示例并根据您的需要进行调整。
免责声明:我是 lombok 的核心贡献者,但这是我能想象到的低优先级,在我们到达 'out of memory issues if you try to do a 1500-size immutablemap builder in source' 之前,我们有大量的错误报告需要挖掘.
*) 可能; class 文件也有限制,如果你删除 lombok 或添加内存,编译器可能会在后面的阶段最终拒绝编译这段代码,因为它会超过你初始化时的 64k 字节码限制。
我同意上面@rzwitserloot 的观点,但有些事情你可以尝试:
- 只需将
-Xss4m
(或更高版本)添加到 运行 您构建的 jvm 即可增加堆栈大小。
- 编辑 由于枚举本身很小,您可以这样做:
package org.example;
import java.util.HashMap;
import java.util.Map;
public enum Animal {
BEAR("white bear", "black bear", "brown bear"),
DOG("mongrel", "cross"),
CAT("blah3");
private final String[] blah1;
private static final Map<String, Animal> MAPPING = new HashMap<>();
static {
for (Animal animal: Animal.values()) {
for (String tag : animal.blah1) {
MAPPING.put(tag, animal);
}
}
}
Animal(String... blah1) {
this.blah1 = blah1;
}
public static void main(String... s) {
String animal = "black bear";
System.out.println(" A " + animal + " is a " + MAPPING.get(animal));
}
}
我有一个 class 看起来像这样:
public enum Animal {
BEAR,
SHEEP,
LION;
private static final Map<String, Animal> MAPPING = new ImmutableMap.Builder<String, Animal>()
.put("BLACK_BEAR", BEAR)
.put("WHITE_BEAR", BEAR)
...
.put("African Lion", LION)
.build()
}
大约 1500 个地图条目。
我尝试构建我的应用程序,但出现以下错误:
[javac] An annotation processor threw an uncaught exception.
[javac] Consult the following stack trace for details.
[javac] java.lang.WhosebugError
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
[javac] at lombok.javac.JavacAST.drill(JavacAST.java:374)
[javac] at lombok.javac.JavacAST.buildStatementOrExpression(JavacAST.java:340)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:200)
[javac] at lombok.javac.JavacAST.buildTree(JavacAST.java:67)
[javac] at lombok.core.AST.buildWithField0(AST.java:386)
[javac] at lombok.core.AST.buildWithField(AST.java:284)
我尝试将地图的大小减小到 15 左右,但构建良好。我们可以对静态不可变映射进行硬编码的大小是否有限制?我没有在文档中看到任何限制。可能是什么问题?
不是ImmutableBuilder;事实上,你的源文件现在是一堆巨大的 AST 节点,lombok 的处理最终占用了太多内存,我猜,考虑到 javac 可以处理它,这在技术上是 lombok 中的一个错误。换句话说,编译器无法将您的 java 文件转换为 class 文件,但如果可以,它会工作得很好*。
我通常会将任何试图在源中定义那么多数据的源文件视为代码味道。
我建议你把这是什么数据放在一个文本文件中。
将此文本文件与您的源文件放在一起;如果你使用 maven / 或多或少的标准源代码目录结构,你会说:
src/main/java/com/foo/yourpkg/Animal.java
src/main/resources/com/foo/yourpkg/AnimalData.txt
这将自动导致 txt 文件与您的 class 文件位于同一位置,即使在 jars 中也是如此。然后,从您的动物文件中阅读它:
public enum Animal {
private static final Map<String, Animal> MAPPING;
static {
try {
Map<String, String> example = new HashMap<String, String>();
try (var in = Animal.class.getResourceAsStream("AnimalData.txt")) {
BufferedReader br = new BufferedReader(new InputStreamReader(
in, StandardCharsets.UTF_8));
for (String line = br.readLine(); line != null; line = br.readLine()) {
String[] p = line.split("\t", 2);
map.put(p[0], p[1]);
}
}
MAPPING = Collections.unmodifiableMap(map);
} catch (IOException e) {
throw new InternalError("data corrupted");
}
}
}
这个假设的例子需要您在文本文件中放入 tab-separated 个字符串对。您可以使用此示例并根据您的需要进行调整。
免责声明:我是 lombok 的核心贡献者,但这是我能想象到的低优先级,在我们到达 'out of memory issues if you try to do a 1500-size immutablemap builder in source' 之前,我们有大量的错误报告需要挖掘.
*) 可能; class 文件也有限制,如果你删除 lombok 或添加内存,编译器可能会在后面的阶段最终拒绝编译这段代码,因为它会超过你初始化时的 64k 字节码限制。
我同意上面@rzwitserloot 的观点,但有些事情你可以尝试:
- 只需将
-Xss4m
(或更高版本)添加到 运行 您构建的 jvm 即可增加堆栈大小。 - 编辑 由于枚举本身很小,您可以这样做:
package org.example;
import java.util.HashMap;
import java.util.Map;
public enum Animal {
BEAR("white bear", "black bear", "brown bear"),
DOG("mongrel", "cross"),
CAT("blah3");
private final String[] blah1;
private static final Map<String, Animal> MAPPING = new HashMap<>();
static {
for (Animal animal: Animal.values()) {
for (String tag : animal.blah1) {
MAPPING.put(tag, animal);
}
}
}
Animal(String... blah1) {
this.blah1 = blah1;
}
public static void main(String... s) {
String animal = "black bear";
System.out.println(" A " + animal + " is a " + MAPPING.get(animal));
}
}