【问题标题】:Getting all combination of 2 elements from a string array java从字符串数组java中获取2个元素的所有组合
【发布时间】:2017-10-28 09:15:54
【问题描述】:

假设我有这个数组列表 ['a', 'b', 'xx']。 我想提取每 2 个字符串组合(每 2 个元素)。例如 ['a','b'] ['a', 'xx'] ['b', 'a'] ['b', 'xx'] ['xx', 'a'] ['xx ', 'b']。

我已经编写了这段代码,但是当数组变得非常大时(10k 例如)GC 耗尽内存。

        private Text empty = new Text("");

    public void start(Iterable<Text> values,Context context) throws {
        List<String> sitesArr = new ArrayList<String>();
        HashMap<String, String> hmapPairs = new HashMap<String, String>();

        for (Text site : values){
            sitesArr.add(site.toString());
        }

        insertPairsToHash(hmapPairs, sitesArr);
        writeContextFromHash(hmapPairs, context);
    } 

    private void insertPairsToHash(HashMap<String, String> hmapPairs, List<String> sitesArr) {
        for (int i=0; i<sitesArr.size(); i++) {
            for (int j=i+1; j<sitesArr.size(); j++) {
                String firstPair = sitesArr.get(i) + "_" + sitesArr.get(j);
                String secondPair = sitesArr.get(j) + "_" + sitesArr.get(i);
                hmapPairs.put(firstPair,secondPair);
            }      
        }
    }

    private void writeContextFromHash(HashMap<String, String> hmapPairs, Context context) throws IOException, InterruptedException {
        Text textTowriteToFile = new Text("");
        for(Map.Entry<String, String> entry : hmapPairs.entrySet()) {
            textTowriteToFile.set(entry.getKey());
            context.write(textTowriteToFile, empty);
            textTowriteToFile.set(entry.getValue());
            context.write(textTowriteToFile, empty);
        }
    }

我使用 2 个 for 循环,在每次迭代中我插入 2 个组合( ['a', 'b'] 和 ['b','a'] 第一个元素是键,第二个是值,所以在 [ 'a','b'] 'a' 将是键,'b' 将是值,反之亦然)到散列。 然后我遍历散列一次以发送值。 如何在使用更少内存的同时使其更快?

【问题讨论】:

  • 我不认为 insertPairsToHash() 做你想做的事:计算所有可能的有序依赖组合。
  • 仅适用于 2 双
  • 你有内存问题吗?抛出内存错误时,您应该提供更多信息,例如 stracktrace。

标签: java arrays algorithm hash hashmap


【解决方案1】:

您可以简单地重构您的课程流式传输结果。因此,您不会保留组合元素的整个结果列表。

    private Text empty = new Text("");

   public void start(Iterable<Text> values,Context context) throws IOException, InterruptedException  {
       List<String> sitesArr = new ArrayList<String>();

       for (Text site : values){
           sitesArr.add(site.toString());
       }
       insertPairsToHash(sitesArr,context);
   } 

   private void insertPairsToHash(List<String> sitesArr, Context context) {
       for (int i=0; i<sitesArr.size(); i++) {
           for (int j=i+1; j<sitesArr.size(); j++) {
               String firstPair = sitesArr.get(i) + "_" + sitesArr.get(j);
               String secondPair = sitesArr.get(j) + "_" + sitesArr.get(i);

               doWrite(context, firstPair, secondPair);
           }      
       }
   }

    private void doWrite(Context context, String firstPair, String secondPair) {
        Text textTowriteToFile = new Text("");
           textTowriteToFile.set(firstPair);
           context.write(textTowriteToFile, empty);
           textTowriteToFile.set(secondPair);
           context.write(textTowriteToFile, empty);
    }

这会降低你的内存使用率。

一般来说,如果您的输入很大或没有限制,您会尝试流式传输结果,流式传输会增加一些复杂性,但会保持内存使用量与您输入的大小无关。

编辑(评论后):

您可以通过从列表中删除已使用的元素来删除它们。 在这种情况下,您应该使用 LinkedList 而不是 ArrayList,因为从数组列表中删除头元素会比从链接列表中执行相同的操作需要更多的 GC 和 CPU 时间。

但是,这不会降低峰值内存使用量,只会降低一段时间内的使用量(随着进程的进行,您将需要更少的内存)。

如果其他组件随着进程的进行消耗更多内存,它仍然很有用。

 List<String> sitesArr = new LinkedList<>();

private void insertPairsToHash(List<String> sitesArr, Context context) {
    while (!sitesArr.isEmpty()) {
        String left = sitesArr.remove(0);
        for (String right : sitesArr) {
            String firstPair = left + "_" + right;
            String secondPair = right + "_" + left;
            doWrite(context, firstPair, secondPair);
        }
    }
}

【讨论】:

  • 是的,在 tsolakp 响应后,我就这样做了,您能想出一个更好的方法来提取组合吗?谢谢
【解决方案2】:

您可能应该在您的问题中添加更多信息。但基本上使用这种程序,随着输入变大,您总是会遇到内存问题。如果有 10k 个条目,您最终会得到大约 100m 个组合,从而产生 50m 个 Map 条目。乘以数据结构的大小(取决于您的输入),这会占用大量内存。 如果您事先知道输入的粗略大小,您可能只需为 jvm 分配足够的内存(除非您的机器很小)。如果这不能解决问题,您将无法将所有结果保存在内存中。要么换出磁盘,要么按照上面的建议将结果直接写入控制台,而不是将它们保存在内存中。

【讨论】:

  • 谢谢,按照其他回复中的建议写入控制台
【解决方案3】:

为什么不在嵌套的 for 循环中直接调用“writeContextFromHash”而不创建 HashMap?

【讨论】:

  • 你完全正确! HashMap 占用太多内存。这样就解决了内存问题。你能想出一种更快的方法来获得所有想要的值吗?
  • 您可以做的另一件事是根本不创建“sitesArr”并在嵌套循环中迭代“值”。这与您的循环逻辑不同,但考虑到原始预期结果,您可以通过 n*n 次迭代来实现它,这可能有点太长,但可以通过不必在列表中存储 10k 个元素来节省内存。
猜你喜欢
  • 1970-01-01
  • 2017-10-11
  • 2015-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-16
  • 1970-01-01
相关资源
最近更新 更多