Java中的flyweight字符串何时有用?

Bra*_*ace 4 java string design-patterns

我理解java的字符串实习的基本思想,但我想弄清楚它发生在哪些情况,以及我需要做哪些自己的轻量级.

有点相关:

他们一起告诉我这String s = "foo"很好而且String s = new String("foo")很糟糕,但没有提到任何其他情况.

特别是,如果我解析一个有很多重复值的文件(比如一个csv),Java的字符串实习会覆盖我还是我需要自己做一些事情?关于字符串实习是否适用于我的其他问题,我得到了相互矛盾的建议


完整的答案有几个片段,所以我在这里总结一下:

默认情况下,java仅实现编译时已知的字符串. String.intern(String)可以在运行时使用,但它执行得不是很好,因此它只适用于较小数量的Strings,你肯定会重复很多次.对于较大的Strings系列,它是拯救的番石榴(参见ColinD的回答).

Col*_*inD 20

Guava为您提供的一个选择是使用Interner而不是使用String.intern().String.intern()与之不同,Guava Interner使用堆而不是永久生成.此外,您可以选择String使用弱引用来实现Strings,这样当您使用这些引用时,Interner不会阻止它们被垃圾收集.但是,如果你使用Interner这种方式,当你完成字符串时它就被丢弃了,你可以使用强引用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
Run Code Online (Sandbox Code Playgroud)


Rol*_*lig 7

不要在代码中使用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
}
Run Code Online (Sandbox Code Playgroud)

使用该代码,您可以避免一个CSV文件的重复字符串.如果您需要更大规模地避开它们,请pool.clear()在另一个地方打电话.

  • 如果您使用套装,如何将实习版本退出?也就是说,你用什么代替`pool.get()`? (4认同)
  • 请注意,为此使用`HashMap`不是线程安全的(尽管对于此示例,显然不需要线程安全性).如果需要,应该使用`ConcurrentMap`和`putIfAbsent`.Guava在其"Interner"实现中为您做到这一点. (4认同)
  • 您可以考虑使用WeakHashMap.另请参阅字符串中的相关问题 - http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622. (2认同)