【问题标题】:Java - how to efficiently store a large amount of String arraysJava——如何高效存储大量String数组
【发布时间】:2012-10-31 05:25:12
【问题描述】:

我正在尝试使用 Java 有效地加载大型 CSV 格式文件(通常为 200-600mb)(更少的内存和尽可能快的访问速度)。目前,该程序正在使用字符串数组列表。此操作之前由 Lua 程序处理,每个 CSV 行使用一个表,每个“行”表使用一个表。

以下是内存差异和加载时间的示例:

  • CSV 文件 - 232mb
  • Lua - 549mb 内存 - 157 秒加载
  • Java - 1,378mb 内存 - 12 秒加载

如果我没记错的话,Lua 表中的重复项作为对实际值的引用存在。我怀疑在 Java 示例中,List 保存每个重复值的单独副本,这可能与较大的内存使用量有关。

以下是 CSV 文件中数据的一些背景信息:

  • 每个字段由一个字符串组成
  • 每行中的特定字段可能包括一组字符串中的一个(例如,字段 3 可以是“红色”、“绿色”或“蓝色”)。
  • 内容中有很多个重复的字符串。

以下是加载数据可能需要的一些示例:

  • 搜索所有试图与给定字符串匹配的字符串并返回匹配的字符串
  • 在 GUI 表格中显示匹配项(可通过字段排序)。
  • 更改或替换字符串。

我的问题 - 是否有一个集合需要更少的内存来保存数据,但仍提供轻松快速地搜索/排序数据的功能?

【问题讨论】:

  • 如果您知道第 3 列仅包含几个可能的值,您可以intern them 以减少内存使用量。另见:stackoverflow.com/a/1855195/829571
  • 谢谢 assylias 我将使用它运行一些测试。你知道它是否对短字符串有效 - 例如“去”或“去”。大多数字段包含超过 45 个字符的字符串,但有些字段很短(4 个或更少)。
  • @PeterLawrey 不错 - 与 intern() 相比,它的表现如何?
  • @assylias 它速度更快,可扩展性更好,但它只能在尽力而为的基础上工作,如果您的大小小于唯一对象的数量,您将获得所有重复项。

标签: java csv lua


【解决方案1】:

一个简单的解决方案。您可以拥有一些HashMap,如果您将引用所有唯一字符串。 而在ArrayList 中,您只需引用HashMap 中现有的唯一字符串。

类似:

private HashMap<String, String> hashMap = new HashMap<String, String>();

public String getUniqueString(String ns) {
   String oldValue = hashMap.get(ns);
   if (oldValue != null) { //I suppose there will be no null strings inside csv
    return oldValue;
   }        
   hashMap.put(ns, ns);
   return ns;
}

简单用法:

List<String> s = Arrays.asList("Pera", "Zdera", "Pera", "Kobac", "Pera", "Zdera", "rus");
List<String> finS = new ArrayList<String>();
for (String er : s) {
   String ns = a.getUniqueString(er);
   finS.add(ns);
}

【讨论】:

  • 听起来就像您正在尝试优化已经被 java 优化的东西(为内存中的重复字符串节省内存),不需要这样的实现,请参阅我的答案
【解决方案2】:

也许这篇文章可以提供一些帮助:

http://www.javamex.com/tutorials/memory/string_saving_memory.shtml

【讨论】:

  • 我最终尝试了文章中介绍的两个示例。事实证明,intern() 节省了最多的内存。我将继续试验(尤其是在我完成更多项目之后),但这无疑使我的内存使用与 Lua 保持一致,尽管加载时间更快。
  • 这就是为什么你不应该只做链接的答案 - 链接现在已经死了。
【解决方案3】:

DAWG

有向无环词图是存储词的最有效方式(无论如何最适合内存消耗)。

但这里可能有点矫枉过正,正如其他人所说,不要创建重复项,只需对同一个实例进行多次引用。

【讨论】:

  • 谢谢,我会再研究一下这个选项。我还不会认为有什么矫枉过正的事情 - 效率越高,每个会话可以加载的数据越多,这对最终用户来说更好。
【解决方案4】:

为了优化您的内存问题,我建议使用Flyweight 模式,特别是对于有很多重复项的字段。

作为集合,您可以使用TreeSetTreeMap

如果您对 LineItem 类(实现 equalshashcodeComparable)进行了良好的实现,则可以大大优化内存使用。

【讨论】:

    【解决方案5】:

    只是作为旁注。

    对于您怀疑的重复字符串数据,您不必担心,因为 java 本身关心这一点,因为所有字符串都是最终的,并且所有引用都针对内存中的同一个对象。

    所以不确定 lua 是如何完成这项工作的,但在 java 中它应该也很高效

    【讨论】:

    • 但如果这是真的,那么就完全没有必要使用 == 来进行比较
    • 好吧,equals 是正确的方法,因为它是你应该在 java 中比较对象的方法,== 也可以,但它只是一种副作用,由于 JVM 内部处理字符串的方式跨度>
    • 好吧,我不确定 java vm 内部有多少内存用于保存字符串引用,但我很确定在足够大的程序中 == 将不起作用
    • 你在开玩笑吧?请参阅:stackoverflow.com/questions/767372/java-string-equals-versus(Michal Bernhard 的回复),为什么 JVM 会以这种优化的方式只引用一些字符串(不是全部)?
    • 是的,但在这个问题示例中,我很确定@user1816198 不会有一堆静态的“Some String”字符串,而是满载的动态字符串(我想他将使用 StringBuilders 或其他东西解析csv)。试试这个简单的程序 String a = "Helo,what s up,baby,Hello,baby";字符串[] b = a.split(","); for (String c : b) { System.out.println(c); } if (b[0] == b[3]) { System.out.println("Equals Hello"); } if (b[2] == b[4]) { System.out.println("Equals baby"); } 最后两个 if 在我的机器上是假的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-04-09
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    • 2015-09-30
    • 2011-12-06
    相关资源
    最近更新 更多