使用Java对数百万个int / string对进行排序

时间:2012-05-22 15:03:57

标签: java sorting

我在文本文件中有50,000,000(整数,字符串)对。整数是以毫秒为单位的时间,因此长度为13位(例如1337698339089)。

文本文件中的条目如下:

1337698339089|blaasdasd
1337698339089|asdasdas
1337698338089|kasda

可以有相同的条目。

我想对整数上的条目(按升序排序)进行排序,保留任何重复的整数并保留(整数,字符串)对。我采取的方法导致内存错误,所以我正在寻找替代方法。

我的方法是这样的(使用一些伪代码):

// declare TreeMap to do the sorting
TreeMap<Double, String> sorted = new TreeMap<Double, String>();

// loop through entries in text file, and put each in the treemap:
for each entry (integer, string) in the text file:

   Random rand = new Random();
   double inc = 0.0;

   while (sorted.get(integer + inc) != null) {
       inc = rand.nextDouble();
   }

   sorted.put(integer + inc, string);

我在这里使用随机数来确保可以在树形图中输入重复的整数(通过将它们在0和1之间递增)。

// to print the sorted entries:
for (Double d : sorted.KeySet()) {
    System.out.println(Math.round(d) + "|" + sorted.get(d));
}

这种方法有效,但会分解50,000,000个条目(我认为因为树形图变得太大;或者可能因为while循环运行的时间太长)。

我想知道更有经验的程序员会采用什么方法。

非常感谢!

8 个答案:

答案 0 :(得分:13)

如果您有足够的内存,您应该可以使用列表执行此操作。我会为条目创建一个单独的类:

class Foo : Comparable<Foo> {
    private final long time;
    private final String text;

    // Constructor etc
}

就内存而言,您需要能够存储5000万个实例,并对它们进行引用。在32位JVM上,这将是:

  • 每个对象8个字节的开销(IIRC)
  • time
  • 的8个字节
  • text字段
  • 的4个字节
  • 〜字节的54个字节(8字节开销+ 3个int字段IIRC + char[]数组引用+ 10字符数组的~32字节)
  • 4个字节用于数组中的引用或ArrayList

因此,每个实例大约有80个字节 - 比如100个向上舍入。要存储50,000,000个字节,需要5,000,000,000字节,即5GB,这比我认为的32位JVM还要多。

所以要在内存中完成所有这些操作,你需要一台64位机器和64位JVM,然后由于更大的引用等原因,开销可能会有所增加。可行,但不是非常令人愉快。

然而,很大一部分是由于字符串。如果你真的想要高效,你可以创建一个巨型字符数组,然后在Foo内存储偏移量。在读取文本数据时读入数组,然后在排序后使用它来写出数据。更复杂,更丑陋,但内存效率更高。

或者,您可以在内存中执行而不是 - 我相信如果您在周围搜索,您将找到有关通过文件系统进行排序的大量信息。

答案 1 :(得分:2)

我可能会考虑使用数据库(比如H2;这很方便,因为你可以将它直接引入Java项目中)并按照你想要的方式设置索引。数据库已经解决了处理大量数据和组织数据的问题。然后,您可以执行SQL查询以按顺序获取结果并将其写回。

结果集将以块的形式将数据流式传输给您;不要试图将所有内容加载到单个列表中。

虽然H2确实支持记忆;在这种情况下,我会将它配置为使用磁盘,除非你有大量的RAM和64位Java。

答案 2 :(得分:1)

为什么使用double来存储long

Map<Long, String>不能有重复的密钥。一个会覆盖另一个。

我怀疑你能把所有这些都融入记忆中。这只是0.5 GB用于存储长信息,更多用于存储字符串。您可能无法使用32位JVM。

答案 3 :(得分:1)

你给了JVM更多的内存吗?尝试使用-Xmx1024M命令行选项运行它。而treeMap似乎不必要复杂,你可以使用内置的Java命令

答案 4 :(得分:1)

您的问题看起来分为两部分:

  1. 算法:我建议使用一些内置的java排序算法。在Google上很容易找到引用,例如this
  2. JVM:问题的根源听起来可能没有足够的内存分配给您的java虚拟机。我建议增加最大尺寸,因为你正在处理下降量的信息。
  3. 你正在寻找的JVM args应该是:

    • -Xms 指定初始Java堆大小和

    • -Xmx 最大Java堆大小。

    参考:http://www.rgagnon.com/javadetails/java-0131.html

答案 5 :(得分:0)

抛出的错误是什么?你能成功地将所有数据加载到内存中吗? 我建议你尝试Java Comparator类。也许我会尝试创建一个自定义对象来代表这对:

class Entry{
    long i;
    String s;
}

然后创建自定义比较器

class IComp implements Comparator<Entry>{
    public int compare(Entry e1, Entry e2){
      if(e1.i < e2.i) return -1;
      //complete the rest

    }
}

然后将所有对象放入数组Entry []条目,并创建一个比较器IComp icomp 使用Arrays.sort(entry,icomp)

由于您将创建5000万个对象,因此需要确保有足够的堆空间。

如果你有大量重复的字符串,并且这些字符串是不可变的;您可以创建一个Set来存储字符串,并回收它们以在条目中创建较轻的对象

Entry.s = set.get()...

答案 6 :(得分:0)

我希望通过对数据块进行排序并将它们写入不同的文件并对这些文件应用合并排序来解决这个问题。这是working demo,这可能对您的方案有所帮助。

答案 7 :(得分:0)

在您完成排序时,我不确定您是否要使用所有值。但是数字5000万给了我一个暗示,你可能会在排序之后拿出前X值,然后用它们做点什么。

在这种情况下:只需使用最小堆,每次遇到大于堆顶部的数字时,从堆中删除min并添加新数字。这样您就不必将所有数字保存在内存中,只需要保留X中的数字。