代码之家  ›  专栏  ›  技术社区  ›  Dan

Java中字符串flyweight实现的最佳选择

  •  9
  • Dan  · 技术社区  · 16 年前

    我已经看过Java常量池和String.intern,但它似乎会引发一些PermGen问题。

    How does java implement flyweight pattern for string under the hood?

    5 回复  |  直到 9 年前
        1
  •  8
  •   Mark Peters    11 年前

    substring 示例在OpenJDK/oracle7+中不再是一个问题。

    我知道这和人们常说的背道而驰,但有时会明显地创造新事物 String 可以

    由于字符串是不可变的,有几种方法利用这一事实并共享支持字符数组以节省内存。但是,有时这实际上可以通过防止对那些数组中未使用的部分进行垃圾收集来增加内存。

    例如,假设您正在解析日志文件的消息ID以提取警告ID。您的代码如下所示:

    //Format:
    //ID: [WARNING|ERROR|DEBUG] Message...
    String testLine = "5AB729: WARNING Some really really really long message";
    
    Matcher matcher = Pattern.compile("([A-Z0-9]*): WARNING.*").matcher(testLine);
    if ( matcher.matches() ) {
        String id = matcher.group(1);
            //...do something with id...
    }
    

        //...
        String id = matcher.group(1);
        Field valueField = String.class.getDeclaredField("value");
        valueField.setAccessible(true);
    
        char[] data = ((char[])valueField.get(id));
        System.out.println("Actual data stored for string \"" + id + "\": " + Arrays.toString(data) );
    

    这是整个测试行,因为matcher只是在相同的字符数据周围包装一个新的字符串实例。替换时比较结果 String id = matcher.group(1); 具有 String id = new String(matcher.group(1)); .

        2
  •  3
  •   BalusC    16 年前

    这已经在JVM级别完成了。你只需要确保你没有 new String 每次都是,无论是显式的还是隐式的。

    即不要做:

    String s1 = new String("foo");
    String s2 = new String("foo");
    

    这将在堆中创建两个实例。而是这样做:

    String s1 = "foo";
    String s2 = "foo";
    

    s1 == s2 会回来的 true 这里)。

    +=

    String s = "";
    for (/* some loop condition */) {
        s += "new";
    }
    

    这个 隐式创建 新建字符串

    StringBuilder sb = new StringBuilder();
    for (/* some loop condition */) {
        sb.append("new");
    }
    String s = sb.toString();
    

    如果可以的话,不如用 StringBuilder 或者是它的兄弟 StringBuffer 而不是 String 用于“密集字符串处理”。它为这些目的提供了有用的方法,例如 append() insert() , delete() its javadoc

        3
  •  2
  •   Community Mohan Dere    6 年前

    如果你按照公认的答案去做,并且使用Java7或更新版本,你就没有按照它说的去做。

    实施 subString() 已经改变了。

    永远不要编写依赖于可能会发生巨大变化的实现的代码,如果您依赖于旧的行为,可能会使事情变得更糟。

    1950    public String substring(int beginIndex, int endIndex) {
    1951        if (beginIndex < 0) {
    1952            throw new StringIndexOutOfBoundsException(beginIndex);
    1953        }
    1954        if (endIndex > count) {
    1955            throw new StringIndexOutOfBoundsException(endIndex);
    1956        }
    1957        if (beginIndex > endIndex) {
    1958            throw new StringIndexOutOfBoundsException(endIndex - beginIndex);
    1959        }
    1960        return ((beginIndex == 0) && (endIndex == count)) ? this :
    1961            new String(offset + beginIndex, endIndex - beginIndex, value);
    1962    }
    

        4
  •  1
  •   Daniel    16 年前

    有效地在内存中打包字符串!我曾经写过一个超内存效率的集合类,其中字符串以树的形式存储。如果通过遍历字母到达叶子,则该条目包含在集合中。使用起来也很快,是存储大型词典的理想选择。

    别忘了,在我分析过的几乎所有应用程序中,字符串通常是内存中最大的部分,所以如果需要的话,不要在意它们。

    插图:

    你有三根弦:啤酒,豆子和血。可以创建如下树结构:

    B
    +-e
      +-er
      +-ans
    +-lood
    

    非常有效,例如街道名称列表,这显然是最合理的固定字典,因为插入不能有效地完成。实际上,应该创建一次结构,然后序列化,然后加载。

        5
  •  0
  •   Jason    16 年前

    首先,确定如果您消除了一些解析,您的应用程序和开发人员将遭受多大的损失。如果在此过程中员工流失率翻倍,那么更快的应用程序对您没有好处!我想根据你的问题,我们可以假设你已经通过了这个测试。

    第二,如果你不能避免创建一个对象,那么你的下一个目标应该是确保它不会在Eden集合中幸存。解析查找可以解决这个问题。然而,“正确实现”的缓存(我不同意这个基本前提,但我不会用随之而来的咆哮来让你厌烦)通常会带来线程争用。你会把一种记忆压力换成另一种。

    类型安全枚举

    您正在处理的文本是自由格式的,还是输入必须遵循严格的规范?如果你的许多文本都呈现为一组固定的可能值,那么TSE可以在这里帮助你,并为你提供更大的帮助:在创建时而不是在使用时为你的信息添加上下文/语义。

    推荐文章