【问题标题】:Iterate ArrayList and check against all other elements迭代 ArrayList 并检查所有其他元素
【发布时间】:2013-01-17 21:50:19
【问题描述】:

我正在尝试根据所有其他值检查 ArrayList 中的所有值,如果它们的值太接近,则删除一个。这是一个例子:

// make an ArrayList of random numbers
ArrayList<Integer> nums = new ArrayList<Integer>();
for (int i=0; i<25; i++) {
  int rand = int(random(255));
  nums.add(rand);
  println(rand);  
}

// go through all numbers and compare
// (loop backwards to prevent ConcurrentModificationException)
for (int i = nums.size()-1; i >= 0; i--) {
  int current = nums.get(i);
  println("Current #: " + current);

  // how to do this?
  // not sure if there's a faster way that
  // would avoid running through the entire 
  // ArrayList for every element...
  for (Integer other : nums) {
    if (abs(current - other) < 5) {
      nums.remove(current);
    }
  }
}

寻找最干净、最有效的方法。

[为清楚起见进行了编辑]

【问题讨论】:

  • 当前解决方案有什么问题?我看到的唯一选择是首先对 ArrayList 进行排序。
  • 你为什么要抓Exception,是什么让你认为它总是因为项目相同? (提示:查看异常 - 你可能会感到惊讶......)
  • 为什么结果“不满意”?发生了什么你不喜欢的事情?
  • -1 用于鸵鸟/口袋妖怪异常处理(必须全部隐藏)。
  • @JeffThompson 关键是,如果你隐藏所有异常,你就不能合理地调试你的代码——这就是你应该做的。

标签: java arraylist compare distance loops


【解决方案1】:

您最好以不同的方式执行此操作,以避免并发修改和/或超出范围的异常。

在迭代集合时从集合中删除任何内容是一个冒险的想法(恕我直言),将其替换为向另一个集合添加内容要安全得多。

因此,将代码替换为等效代码,但将对象添加到新集合中。

集合是轻量级对象,因此创建它们不会占用太多资源。

最后将原始集合变量分配给新集合。

类似这样的:

        final ArrayList<Integer> nums = new ArrayList<Integer>();
        final ArrayList<Integer> result = new ArrayList<Integer>();
        for (int i = 0; i < 25; i++) {
            final int rand = Double.valueOf(Math.random() * 255).intValue();
            nums.add(rand);
        }
        System.out.println(nums);
        outer: for (Integer current : nums) {
            // result collection is reevaluated here 
            // and is not modified inside the inner for loop
            // so, accessing it is safe
            for (Integer other : result) {
                if (Math.abs(current - other) < 5) {
                    // there is a too close value, do not put, skip the check
                    continue outer;
                }
            }
            // a too close value doesn't exist - add object
            result.add(current);
        }
        // here you may assing new collection to the old reference, uncomment next line
        // nums = results;

【讨论】:

  • 感谢 Alex - 至少对我来说这个版本很有意义。您的意思是即使使用迭代器(或者像我的示例一样只是一个 for 循环)进行迭代时可能会出现问题?另外:在嵌套的 for 循环示例中使用迭代器是否可以节省内存?我将在像素阵列上使用它来处理非常大的图像,所以我正在尝试提前计划!
  • 在我的示例中使用迭代器或仅使用 for 循环并没有太大区别。我认为我的示例中的语法只是一个简短的形式,但在内部它也使用了一个迭代器。迭代器是轻量级对象,使用/不使用它们不会对内存造成太大影响。
  • 关于删除对象 - 您可能根本不注意并从您迭代(使用或不使用迭代器)的集合中删除一个元素,从而导致异常。迭代器没有什么神奇之处,它只是一种对集合进行迭代的便捷形式。您可以简单地使用一些 int i 作为计数器,并通过调用 get(i) 方法循环获取集合的元素。效果是一样的。
  • @AlexKreutznaer 关于迭代器的轻量性:我曾经遇到过一种情况,即迭代器是在一个紧密的循环中创建的(就像这里的那个)。最终结果是频繁的垃圾收集周期(阅读:每十秒神秘暂停)。因此,在为每一帧创建大量轻量级对象(如迭代器)之前,请三思而后行。
  • @AlexKreutznaer:谢谢 - 这澄清了很多。我认为 Iterator 的 remove() 以不同的方式构建以处理潜在问题,但也许我误解了。无论如何 - 很好的解决方案,非常有帮助!
【解决方案2】:

当您从数组中删除并同时迭代它时,您将获得(并隐藏)大量 java.util.ConcurrentModificationExceptionjava.lang.IndexOutOfBoundsException

为了避免你需要使用迭代器:

final ArrayList<Integer> nums = new ArrayList<Integer>();
    for (int i = 0; i < 25; i++) {
        final int rand = Double.valueOf(Math.random() * 255).intValue();
        nums.add(rand);
    }
    System.out.println(nums);

    for (int i = nums.size() - 1; i >= 0; i--) {
        final int current = nums.get(i);
        // println(current);
        try {
            for (final Iterator<Integer> iterator = nums.iterator(); iterator.hasNext();) {
                final Integer other = iterator.next();
                if (Math.abs(current - other) < 5) {
                    iterator.remove();
                    i--;
                }
            }
        } catch (final Exception cme) {
            System.out.println(cme);
        }
    }

    System.out.println(nums);

【讨论】:

  • 谢谢 - 我有另一个带有迭代器的版本,但对语法不太熟悉,并且出现了很多错误。
猜你喜欢
  • 2020-12-11
  • 2014-08-09
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
  • 2021-10-19
  • 2019-07-16
  • 2018-06-26
  • 1970-01-01
相关资源
最近更新 更多