【问题标题】:finding the non common element between two arrays查找两个数组之间的非公共元素
【发布时间】:2015-07-20 19:35:52
【问题描述】:

在一次采访中,它被要求在两个字符串数组之间找到不常见的元素。

Eg: String a[]={"a","b","c","d"}; 
String b[]={"b","c"}; 
O/p should be a,d

我已经回答了 Java Set 是使用 HashTable 实现的问题。使用 Set 的代码要简单得多:

String[] a = {"a","b","c","d"};
String[] b = {"b", "c"};

Set<String> set = new HashSet<>(a.length);
for(String s : a){
    set.add(s);
}
for(String s : b){
    set.remove(s);
}
return set;

现在我的问题是有没有其他更好的方法来实现这一点

【问题讨论】:

  • 使用这种方法你会错过只存在于b 中的字符串,对吧?
  • 看起来输入是排序的……这是偶然的,还是有保证的?
  • @aioobe 碰巧如果字符串没有排序,请告知方法../
  • @aioobe 好吧,是的,这是偶然的,如果字符串没有排序,请告知该怎么办
  • @aioobe 是的,非常多只包含 a 中的元素,但请告知如果数组未排序,那么在这种情况下我应该怎么做

标签: java collections set


【解决方案1】:

您可以通过以下方式缩短代码

TreeSet set = new TreeSet(Arrays.asList(a));
set.removeAll(Arrays.asList(b));

Demo

【讨论】:

  • @SaurabhJhunjhunwala 一个集合怎么会有重复的?
  • @KickButtowski,我说过,如果列表有重复项,它将不起作用。
  • 重复的相关性如何,@SaurabhJhunjhunwala?任务是寻找非共同元素。这种方法效果很好。
  • 取决于您是否将两个 .equal 元素视为两个不同的元素。但这种观点有点愚蠢。
【解决方案2】:

如果[x,y], [x,z] 应该产生[y,z],这就是我的建议:

String[] a = {"a","b","c","d"};
String[] b = {"b", "c", "x"};

Set<String> set = new HashSet<>(Arrays.asList(a));
for (String s : new HashSet<>(Arrays.asList(b)) {
    if (!set.add(s))    // if it's already present
        set.remove(s);  // remove it from the result
}

另一方面,如果[x,y], [x,z] 应该产生[y],我会建议

Set<String> set = new HashSet<>(Arrays.asList(a));
set.removeAll(Arrays.asList(b));

【讨论】:

  • 如果我错了,请纠正我,但 remove 是多余的,对吧?集合不允许重复。
  • 不。它确保删除重复项。
  • @aioobe set 中的 add 方法不会将已经存在的元素添加到集合中。你能详细说明删除的需要吗?阅读:docs.oracle.com/javase/7/docs/api/java/util/Set.html#add(E)
  • 如果一个元素没有被添加到 Set 中,为什么要删除它?
  • @aioobe Downvote 撤回!这个答案应该是被接受的。
【解决方案3】:

实际上,这扩展了 Jon Skeet 的答案,但使用 Java 8 的流。

String[] result = Arrays.stream(a)
                        .filter((s) -> Arrays.stream(b).noneMatch(s::equals))
                        .toArray(String[]::new);

System.out.println(Arrays.toString(result));

代码的主要租户是:

  • 当且仅当 B 中不存在 A 中包含的任何元素(通过短路终端运算符 noneMatch)时,过滤掉 A 中包含的任何元素,检查该元素是否等于该流中的任何元素。
  • 将结果收集到String[]

使用Set 的另一种方法,并再次使用流:

Set<String> setA = new HashSet<>(Arrays.asList(a));
Set<String> setB = new HashSet<>(Arrays.asList(b));

String[] setResult = setA.stream()
                         .filter((s) -> !setB.contains(s))
                         .toArray(String[]::new);

正如所指出的,非 Set 代码的主要问题是在最坏的情况下它是二次时间。这里的代码利用了对Set#contains 的恒定访问时间,并且应该在大约线性时间内运行。

【讨论】:

  • 请注意,由于您为a 的每个元素迭代b,这会产生二次复杂度。事实上,我不建议在这个用例中使用流。在我看来,这种算法以嵌套 for 循环的形式更具可读性。
  • 这是真的,也很可悲。让我想出一种利用集合的方法。
  • 你不需要通过toList。您可以直接在流上使用toArray
  • 不过,您无法从toArray 获得String[]。您只能获得Object[]。尝试向下转换将导致ClassCastException
  • 您将使用另一个toArray 方法:.toArray(String[]::new)
【解决方案4】:

我会分三步处理这个问题:

  • 查找a 中的所有元素,但不是b
  • 查找b 中的所有元素,但不是a
  • 将这两组加在一起

例如:

Set<String> aSet = new HashSet<>(Arrays.asList(a));
Set<String> bSet = new HashSet<>(Arrays.asList(b));

Set<String> aNotB = new HashSet<>(aSet);
aNotB.removeAll(bSet);

Set<String> bNotA = new HashSet<>(bSet);
bNotA.removeAll(aSet);

Set<String> onlyOne = new HashSet<>(aNotB);
onlyOne.addAll(bNotA);

(Java 8 中的流代码也可能使这变得更简单......)

如果您不介意修改aSetbSet,代码可以变得更短,但我发现这个版本更易于阅读。

【讨论】:

  • yes very much only contain elements in a。我猜 OP 只对等式的一侧感兴趣?
  • @ChetanKinger:这是对 OP 当前代码的作用的评论。我会争辩说它没有回答书面的问题。
【解决方案5】:

试试这个:

String a[]={"a","b","c","d"}; 
String b[]={"b","c"}; 

List aLst = new ArrayList(Arrays.asList(a));
List bLst = new ArrayList(Arrays.asList(b));

aLst.removeAll(bLst);
System.out.println(aLst);

【讨论】:

    【解决方案6】:

    如果字符串只是英文字母(或小字母表......甚至 ASCII),我宁愿使用 boolean[] by char value 而不是 HashSets 等来提高性能。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-15
      • 2017-09-05
      • 2011-01-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多