【问题标题】:Efficient way to find multiple objects in Arraylist在 Arraylist 中查找多个对象的有效方法
【发布时间】:2014-07-10 02:48:33
【问题描述】:

我必须检查一个数组列表是否包含通过对象传递的任何值。

考虑一个值为“abc”、“jkl”、“def”、“ghi”的数组列表。 还有String check="abc,ghi"

我们必须检查字符串中的任何值(abc 或 ghi)是否存在于 arraylist 中,当找到匹配项时我们可以停止检查。

传统上,我们可以用逗号分隔String check,并在迭代中为每个逗号分隔值使用arraylist.contains()

但这很耗时。有没有更好的方法来做这个检查。

【问题讨论】:

  • ArrayList 的最佳搜索时间是 O(n),除非它已排序,在这种情况下你会得到 O(lg n)。是否可以使用HashMapTreeMap
  • 请注意,如果输入是一个 ArrayList,那么对其进行排序将花费 O(n log n) 并将条目放入 HashMap 或 HashSet 将花费 O(n)。你也必须考虑到这一点——你不能只说 HashMap 查找是 O(1) 并完成它。
  • @user3580294 是的,这就是我写“如果输入是 ArrayList”的原因。顺便说一下,在这种情况下,Set 比 Map 更合适。 Maps 用于将键映射到值;集合是用来询问一个值是否存在,这是这里需要的。
  • 您不需要使用HashMap 进行迭代,但是是的,这就是您找到密钥的方式。正如@yshavit 指出的那样,你必须小心——如果你只是为此切换集合,你可能会受到超过你可能获得的任何好处的惩罚。
  • @yshavit 是的,完全错过了……我需要新眼镜。是的,Set 可能效果更好,但如果在这种特定情况下使用Set,则仍然存在复制惩罚(尽管较小),并且Set 可能无法用作@987654331 的替代品@ 一般是因为它有额外的限制。

标签: java string arraylist equals contains


【解决方案1】:

这仍然是 O(n),但您可以从搜索字符串构建一个集合,然后对列表进行一次迭代:

HashSet<String> checks = new HashSet<String>();
checks.addAll(Arrays.asList(check.split(",")));
for (String item : arraylist) {
    if (checks.contains(item)) {
        // Found one
    }
}

【讨论】:

  • 根据讨论,Set 优于 Arraylist。那么,如果Hashset中也有数据,我们是否可以使用equals/contains方法来查找checks中的任何一个字符串是否存在于item中?
  • 有一些有效的方法可以判断两个集合是否有共同的值。但在您的问题中,您有两个列表——arraylist 和split 输出。您必须遍历列表才能将其转换为集合。将两个列表都转换为集合只是为了做集合交集比必要的工作更多。
  • 输入数据可以直接存入hashset而不是存入arraylist。因此,现在它的 hashset 数据和搜索值出现在 String 中,以逗号分隔。喜欢,String check="abc,ghi";
【解决方案2】:

一种方法是使用retainAll 方法和Sets。

示例

// note an additional "ghi" here
List<String> original = new ArrayList<String>(Arrays.asList(new String[]{"abc", "jkl","def", "ghi", "ghi"}));
Set<String> clone = new HashSet<String>(original);
Set<String> control = new HashSet<String>(Arrays.asList(new String[]{"abc","ghi"}));
clone.retainAll(control);
System.out.println(clone.equals(control));

输出

true

【讨论】:

  • 有点吹毛求疵:当您在 clone 上调用 retainAll 时,您实际上使用的是 Set#retainAll 而不是 List#retainAll
  • @Beauflitz 我会这么说,尽管有很多因素需要考虑。可以肯定的是,将Collections 表示为Strings 以比较或操纵它们绝对不是推荐的做法。
  • @blalasaadri 现在刚刚看到您的评论 - 谢谢(删除了模棱两可的部分)。
  • 根据讨论,可以避免arraylist和set之间的切换,数据可以直接存储在set中,所以不需要克隆。现在,我们有 'retainAll()' 和 'equals()',两个操作都需要时间,对吧?我们可以一步一步解决吗?
  • 因为我们可以在Set&lt;String&gt; control中找到一个对象时停止搜索Set&lt;String&gt; clone。 “abc”或“ghi”。不需要两者都存在于父集中。那么,我们怎样才能使这个解决方案更好呢?
【解决方案3】:

您可以将check 转换为正则表达式,并且只在ArrayList 中循环一次。

String check = "abc,ghi";
Pattern p = Pattern.compile("(" + check.replace(',', '|') + ")");
List<String> list = Arrays.asList(new String[] { "abc", "jkl", "def", "ghi" });
for (String element : list) {
    if (p.matcher(element).matches()) {
        System.out.println("match: " + element);
    }
}

【讨论】:

  • 这是使用 arraylist 的最佳方式吗?
  • @Beauflitz 很难说什么是“最好的”。我想这掩盖了你所做的额外循环,但由于我不知道 matches() 方法的工作原理,我无法告诉你这个与原始循环相比的性能特征。
猜你喜欢
  • 1970-01-01
  • 2018-01-06
  • 2010-10-08
  • 2014-12-04
  • 1970-01-01
  • 2018-06-12
  • 1970-01-01
  • 2013-10-12
  • 2018-09-18
相关资源
最近更新 更多