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 的观点,但有些事情你可以尝试:

  1. 只需将 -Xss4m(或更高版本)添加到 运行 您构建的 jvm 即可增加堆栈大小。
  2. 编辑 由于枚举本身很小,您可以这样做:
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));
    }
}