代码之家  ›  专栏  ›  技术社区  ›  Brad Mace Mike King

什么时候在Java中使用flyweight字符串是有益的?

  •  4
  • Brad Mace Mike King  · 技术社区  · 14 年前

    basic idea java的字符串实习,但我想弄清楚它发生在什么情况下,我需要做我自己的flyweighting。

    有点关联:

    他们一起告诉我 String s = "foo" String s = new String("foo") 很糟糕,但没有提到其他情况。

    特别是,如果我解析一个有很多重复值的文件(比如csv),Java的字符串interning会覆盖我还是我需要自己做些什么?我得到了一个相互矛盾的建议,关于是否在我的 other question


    默认情况下,java只对编译时已知的字符串进行实习。 String.intern(String) 可以在运行时使用,但性能不是很好,因此它只适用于较小数量的 String 你肯定会重复的 . 对于更大的琴弦,是番石榴来拯救(见科林的回答)。

    8 回复  |  直到 8 年前
        1
  •  7
  •   Roland Illig    14 年前

    不要在代码中使用String.intern()。如果你可以得到20个或更多不同的字符串,至少不会。根据我的经验 String.intern

    避免重复 String 对象,只需使用 HashMap .

    private final Map<String, String> pool = new HashMap<String, String>();
    
    private void interned(String s) {
      String interned = pool.get(s);
      if (interned != null) {
        return interned;
      pool.put(s, s);
      return s;
    }
    
    private void readFile(CsvFile csvFile) {
      for (List<String> row : csvFile) {
        for (int i = 0; i < row.size(); i++) {
          row.set(i, interned(row.get(i)));
          // further process the row
        }
      }
      pool.clear(); // allow the garbage collector to clean up
    }
    

    使用该代码,您可以避免一个CSV文件的重复字符串。如果你需要在更大范围内避免它们,请致电 pool.clear()

        2
  •  20
  •   ColinD    14 年前

    一种选择 Guava 给你的是用 Interner String.intern() . 不像 String.intern() Interner 使用堆而不是永久生成。此外,您还可以选择 String 字符串 s、 那个 不会阻止他们被垃圾收集。如果你使用 互联网 Interners.newStrongInterner() 而是为了更好的表现。

    Interner<String> interner = Interners.newWeakInterner();
    String a = interner.intern(getStringFromCsv());
    String b = interner.intern(getStringFromCsv());
    // if a.equals(b), a == b will be true
    
        3
  •  2
  •   TofuBeer    14 年前

    这个信息可能已经过时了,我已经没有代码来支持它了。。。

    (没有过时的东西):

    (可能过时的内容):

    我写了一个程序,我想快速,使用尽可能少的内存。每次从文件中读取字符串时,我都尝试过带实习生和不带实习生。实习的方式明显比不用实习生的时间长,以至于我决定不做实习生。如果性能很重要,请尝试在有/没有实习生的情况下计时代码。您可能还想在有/没有intern的情况下检查内存使用情况(profiler会很好地解决这个问题),看看折衷是否对您有影响。

        4
  •  1
  •   Paul Rubel    14 年前

    阅读 String javadoc

    这让我相信,你从文件中得到的字符串,在你的程序编译后,不会被自动执行。

    如果你说什么,

    String x = "string";
    

    这将被编译器截取,因为它在编译时是可见的。

    如果知道某些字符串在输入文件中非常常见,可以调用

    stringFromFile.intern();
    

    这个字符串将被添加到实习生池中供以后使用。甚至可以通过在代码的主部分或静态部分调用intern来预缓存它们。

    您可以尝试对您的特定输入进行实验,看看在最佳情况下,如果您手动实习一些数据,并将其与默认的无实习行为进行比较,会发生什么。

        5
  •  1
  •   micdah    14 年前

    据我所知,string interning只对string文本自动执行,所有其他文本都必须使用{@link java.lang.string#intern()}方法进行编程。因此,通过其构造函数使用已实习的字符串文本构造字符串会生成一个新字符串,该字符串不是实习的,但包含与构造它的实习文本相同的内容。

    javatechniques.com .

        6
  •  1
  •   Nikita Rybak    14 年前

    byte char 数组(除非它是代码中的字符串文本),以便您可以测试它。

        String s = "test";
        String s1 = new String(s.getBytes());
        String s2 = String.valueOf(s.toCharArray());
        String s3 = new String(s.toCharArray());
    
        System.out.println(s == s1);
        System.out.println(s == s2);
        System.out.println(s == s3);
    

    false 为所有人。但是你可以显式地实习字符串,如果你觉得你会有很多重复的值。如果将此添加到上面的示例中,它将打印 true

        s1 = s1.intern();
        s2 = s2.intern();
        s3 = s3.intern();
    

    See String#intern description in the API .

    编辑
    那么对读取的每个值使用intern()是否是实现flyweighting的合理方法?
    是的,假设没有对旧字符串的引用。如果旧的字符串引用不再在任何地方使用,它将被垃圾回收。

        7
  •  1
  •   bwawok    14 年前

    例如。。。批处理代码。您计划处理1亿行,创建的许多pojo都有一个字段(比如person对象上的一个CITY字段),该字段只是少数可能的答案之一(纽约、芝加哥等)。做一个枚举有太多的选择,但是你真的不需要创建4500万个字符串,比如纽约。您可以使用interning或某种home-rolled变体(弱引用映射可能比String.intern更好)来减少内存占用。

    你可以节省内存空间的代价是可能的CPU工作。。。在某些地方是值得的,但很难说。GC很快,重复的字符串一使用完就会得到gcd。

    所以如果你在一个地方遇到内存墙,并且有一个基数很低的字符串。。。你可以考虑实习。

        8
  •  1
  •   maaartinus    7 年前

    我想,在 -XX:StringTableSize 开关, String.intern() 应该是有用的。速度太快的原因是表的大小是固定的,即使没有任何内部连接,也会被字符串值的常量无望地重载。

    桌子的尺寸应该是最好的!

    用大一点的桌子 String.intern() Map.Entry 也不是 WeakReference ).