【问题标题】:ArrayList sorting, application stuckArrayList排序,应用卡住
【发布时间】:2015-04-11 17:39:27
【问题描述】:

我有一个 ArrayList,其中填充了来自文本文件的单词,我需要按单词的出现次数(从出现次数最多的单词到出现次数最少的单词)对其进行排序。我将带有单词的原始 ArrayList 复制到另一个 Arraylist 并在顶部添加出现次数。因此,新 ArrayList 中的单词将如下所示: "password:125" 其中 "password" 是单词,"125" 是 ArrayList 中出现的次数。

for (int i=0;i<sorter.size();i++) {
                    sorter2.add(sorter.get(i)+":"+Collections.frequency(sorter, sorter.get(i)));
                }

然后我用这个类对 ArrayList 进行排序:

public class RepeatFormulaCounter implements Comparator<String> {

    @Override
    public int compare(String o1, String o2) {
        if (findValue(o2) != findValue(o1)) {
            return findValue(o2) - findValue(o1); 
        }
        return o2.compareTo(o1);
    }
    public int findValue(String find){
        int result=0;
        String spliter[]=find.split(":");        
        result=Integer.parseInt(spliter[1]);
        return result;
    }


}

但是,由于我有 5 个填充单词的文本文件,其中 3 个文件大约 45000 个单词,2 个超过 1000000 个单词,大约 45000 个单词的文件被排序和显示没有任何问题,但是当我开始对超过 1 000 000 个单词的应用程序进行排序。为什么会这样?我该如何解决?

请注意我正在使用 GUI 应用程序来显示它。而且我正在使用 2 个类似的排序类,用于按不同标准进行排序的其他方式,这些方式显示和执行没有任何问题。

【问题讨论】:

  • 你在 GUI 中显示什么?您正在使用哪些 GUI 组件?更有可能是 GUI 组件无法处理太多数据点的问题。
  • 因为排序不是一项简单的任务。你用什么算法来排序?无论如何,你应该让你的 compare 函数更快,不要为两个对象调用 findValue() 两次。如果您执行超过 1000 万次,整数解析是相当昂贵的。
  • 我通过附加 10 个单词来显示“JTextArea”上出现频率最高的 10 个单词。
  • 好的。程序可能会卡住,因为它会产生大量垃圾并有很多 GC 周期。您可以使用 jconsole 或 jvisualvm 进行检查。至少您将能够确定程序是否由于 cpu 负载或内存问题而卡住。
  • 谢谢你们的回答,我需要一段时间才能理解并尝试一切。 :)

标签: java sorting arraylist


【解决方案1】:

为什么将单词存储为“password:125”?你的工作效率很低。您必须使用有效的数据结构来存储您的单词的统计信息。使用 Map 接口并选择正确的实现来存储单词及其出现。

Map<String, Integer> wordsMap = new HashMap<String,Double>();

/* Fill the wordsMap with data, then use this function to sort.
  Fill and update value by key is simple:

  wordsMap .put(key, 50); <-- put value
  wordsMap .put(key, map.get(key) + 1); <--- update value

  For example:

  wordsMap .put("google", 0); <-- put value
  wordsMap .put("google", map.get("google") + 1); <--- increment value by 1

*/

public static <K, V extends Comparable<? super V>> Map<K, V> 
    sortByValue( Map<K, V> map )
{
    List<Map.Entry<K, V>> list =
        new LinkedList<>( map.entrySet() );
    Collections.sort( list, new Comparator<Map.Entry<K, V>>()
    {
        @Override
        public int compare( Map.Entry<K, V> o1, Map.Entry<K, V> o2 )
        {
            return (o1.getValue()).compareTo( o2.getValue() );
        }
    } );

    Map<K, V> result = new LinkedHashMap<>();
    for (Map.Entry<K, V> entry : list)
    {
        result.put( entry.getKey(), entry.getValue() );
    }
    return result;
}

// sortByValue(wordsMap);

此外,您可以阅读有关课程Hashtable, LinkedHashMap, TreeMap 的信息,然后选择性能更好的课程。它们实现了相同的 Map 接口,但对 put()、get() 和其他方法的内部实现具有不同的渐近性。

Sun 为每个集合类提供的 Javadocs 通常会准确地告诉您您想要什么。

HashMap,例如:

此实现为基本的 操作(get 和 put),假设散列函数分散 桶中的元素。对集合视图的迭代 所需时间与 HashMap 实例的“容量”成正比 (桶的数量)加上它的大小(键值的数量 映射)。

树图:

此实现为 containsKey、get、put 和 remove 操作。

树集:

此实现为基本的 操作(添加、删除和包含)。

Read more about this.

如果它仍然比您的预期慢,您可以使用多线程。如果您有 8 个内核的处理器,您可以将文件拆分为 8 个和平,在 8 个线程中计算字数,合并结果,然后运行排序。

【讨论】:

  • 那么排序部分呢?
  • 感谢您的回答,它让我走上了正确的道路。
【解决方案2】:

很可能是内存问题。尝试增加您的 jvm 堆大小。你制作了大量的临时字符串,你的垃圾收集器会在大数据量时发疯。

【讨论】:

    【解决方案3】:

    我认为问题可能不在所示代码之外,但您可以尝试通过减少 find 调用的数量和创建的对象数量来减少对象垃圾(目前,每个 find 调用都会创建 3 个新对象,而您调用 find 4 次比较):

    @Override
    public int compare(String o1, String o2) {
        int f2 = findValue(o2);
        int f1 = findValue(o1);
        if (f2 != f1) {
            return f2 - f1; 
        }
        return o2.compareTo(o1);
    }
    
    public int findValue(String find){
        int result = 0;
        int cut = find.lastIndexOf(':');
        result = Integer.parseInt(find.substring(cut + 1));
        return result;
    }
    

    这可能可以通过摆脱子字符串来改进......

    可能更好的选择是将用于计数的地图交给比较器构造函数,然后在比较器中使用它:

    public class CountComparator implements Comparator<String> {
      Map<String, Integer> counts;
      public CountComparator(Map<String, Integer> counts) {
        this.counts = counts;
      }
    
      public int compare(String o1, String o2) {
        int f2 = counts.get(o2);
        int f1 = counts.get(o1);
        if (f1 != f2) {
          return f2 - f1;
        } 
        return o2.compareTo(o1);
      }
    }
    

    【讨论】:

    • 谢谢你的回答,它也以不同的方式帮助了我。
    • 您可能希望对所有有用的答案进行投票并接受解决问题的答案...O:)
    【解决方案4】:

    利用 Java 8 中引入的流。它们非常适合处理数据。

    HashMap<String, Integer> occurences = new HashMap<>();
    ...
    Stream<String> stream = occurences.entrySet().stream()
        .sorted((a, b) -> b.getValue() - a.getValue())
        .map(kv -> kv.getKey());
    String[] sortedWords = stream.toArray(size -> new String[size]);
    

    【讨论】:

    • 我刚刚测试了该代码的性能。即使 HashMap 包含 100 万个不同的字符串,它也只需要不到 1 秒的时间并消耗不到 200 MB 的 RAM。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多