【问题标题】:Assert List<List<String>> contains List<String> with no order断言 List<List<String>> 包含没有顺序的 List<String>
【发布时间】:2017-06-15 13:06:42
【问题描述】:

我有一个List&lt;List&lt;String&gt;&gt;,其中包含如下示例数据:

("T1#R1", "T1#R1", "T1#R3", "T1#R4")
("T1#R1", "T1#R1", "T1#R3", "T1#R5")
("T1#R1", "T1#R1", "T1#R6", "T1#R4")
("T1#R1", "T1#R1", "T1#R6", "T1#R5")

我需要断言,List&lt;String&gt; 存在于上述示例中,但未考虑顺序。

例如,以下列表 ("T1#R1", "T1#R1", "T1#R4", "T1#R3") 应被视为存在于 List&lt;List&lt;String&gt;&gt; 中,因为它将包含与第一个列表相同的项目,但顺序不同。

另一方面,("T1#R1", "T1#R3", "T1#R4", "T1#R3") 不应被视为存在于列表中,因为它具有相同的项目,但计数不同。

我知道我可以以编程方式执行此操作,但是如果有 Matcher 可以提供帮助,我会徘徊。

我见过这样的断言:

assertThat(myList, containsInAnyOrder(anotherList.toArray())

但这只会将一个列表与另一个列表进行比较,而不是列表列表中的列表。

PS:我用的是Java6,hamcrest-core-1.3,testng-5.14.1

【问题讨论】:

  • 我会遍历您的列表,将每个列表包装成 Set 以及检查和比较集合的列表,因为没有订单规范
  • 我可能需要更改我的示例,因为列表可能包含重复元素,因此 Set 不必计算列表中元素的重复次数。

标签: java assert matcher hamcrest


【解决方案1】:

我不知道有任何匹配器可以做你想做的事,所以恐怕你必须对其进行编程。

我只需对目标列表进行排序,然后迭代子列表直到找到匹配项:

List<String> target = new ArrayList<>(anotherList);
target.sort();

boolean result = myList.stream()
    .anyMatch(sublist -> equalsInAnyOrder(sublist, target));

equalsInAnyOrder 方法如下:

public <T> boolean equalsInAnyOrder(List<T> sublist, List<T> target) {

    List<String> copy = new ArrayList<>(sublist);
    copy.sort();

    return copy.equals(target);
}

这会对每个子列表进行排序,并将其与目标排序列表进行比较,因此它不是性能方面的,但至少它是简单而简洁的代码。


根据 OP 针对 Java 6 的需要进行编辑:

逻辑与 Java 8 版本中的完全相同。首先对目标列表进行排序,然后比较每个子列表,直到找到匹配项:

List<String> target = new ArrayList<>(anotherList);
Collections.sort(target);

stream()anyMatch 现在变成了 while 循环:

boolean match = false;
Iterator<List<String>> it = myList.iterator();
while (it.hasNext() && !match) {
    List<String> sublist = it.next();
    match = equalsInAnyOrder(sublist, target);
}

现在方法 equalsInAnyOrder 看起来像这样:

public <T> boolean equalsInAnyOrder(List<T> sublist, List<T> target) {

    List<String> copy = new ArrayList<>(sublist);
    Collections.sort(copy);

    return copy.equals(target);
}

【讨论】:

  • 好主意,比我认为的解决方案开销少
  • @StefanWarminski 您的解决方案可能会有更多开销,因为您正在流式传输每个子列表,但它是 O(n x m),而我的是 O(n x m log m),因此您的解决方案更适合更大的列表。
  • 这个解决方案对我来说看起来更“可读”,并且也适用于 Java 6,这就是为什么我将其标记为正确的解决方案。
【解决方案2】:

我没有得到单行解决方案,但我的测试通过了。

我遍历列表列表,将每个列表包装到一个映射(条目是键,计算值)以及要检查的列表。现在我可以检查是否相等:

public void listOfLists() throws Exception {
    List<List<String>> myList = Arrays.asList(
        Arrays.asList("T1#R1", "T1#R1", "T1#R3", "T1#R4"),
        Arrays.asList("T1#R1", "T1#R1", "T1#R3", "T1#R5"),
        Arrays.asList("T1#R1", "T1#R1", "T1#R6", "T1#R4"),
        Arrays.asList("T1#R1", "T1#R1", "T1#R6", "T1#R5"));
    List<String> easy = Arrays.asList("T1#R1", "T1#R1", "T1#R4", "T1#R3");
    List<String> duplicate = Arrays.asList("T1#R1", "T1#R5", "T1#R1", "T1#R6");
    List<String> noMatch = Arrays.asList("T1#R1", "T1#R5", "T1#R6");

    Map<String, Integer> easyCount = countEntries(easy);
    Map<String, Integer> duplicateCount = countEntries(duplicate);
    Map<String, Integer> noCount = countEntries(noMatch);

    for (List<String> l : myList) {
        Map<String, Integer> countedEntries = countEntries(l);
        if (countedEntries.equals(easyCount)) {
            System.out.println("easy matches");
        }
        if (countedEntries.equals(duplicateCount)) {
            System.out.println("duplicate matches");
        }
        if (countedEntries.equals(noCount)) {
            System.out.println("Damn!");
        }
    }
}

private Map<String, Integer> countEntries(List<String> original) {
    return original.stream()
        .collect(Collectors.toMap(Function.identity(), s -> 1, Integer::sum));
}

打印出来

easy matches
duplicate matches

【讨论】:

  • 哦,亲爱的...现在您已将帖子更新为 java 6...如果它对您有所帮助,我不会删除答案
【解决方案3】:

我不知道有任何 Matcher 这样做,你是否关心重复? (你的字符串中会有重复的值吗?)

我会去做这样的事情:

private boolean containsInAnyOrder(List<List<String>> container, List<String> list)
{
  for (List<String> list1 : container)
  {
     if(list.size()==list1.size())
     {
        if(list1.containsAll(list))
        {
           return true;
        }
     }
  }
  return false;
}

这只有在你不关心重复元素并且存在计数时才有效。

("T1#R1", "T1#R1", "T1#R1", "T1#R2")
("T1#R1", "T1#R2", "T1#R2", "T1#R1")

例如,将返回 true。你会重复吗? (对于 Set 解决方案,同样的原因,它不会起作用)

如果出现重复,您需要计算每个项目:

private boolean containsInAnyOrder(List<List<String>> container, List<String> list)
{
  for (List<String> list1 : container)
  {
     if(list.size()==list1.size())
     {
        boolean found = true;
        for(String string : list)
        {
           if (list.stream().filter(pS -> pS.equals(string)).count() != list1.stream().filter(pS -> pS.equals(string)).count())
           {
              found = false;
           }
        }
        if(found)
        {
           return true;
        }
     }
  }
  return false;
}

注意它没有优化,内部for循环可能会在不匹配的情况下停止。

【讨论】:

  • 我可以有重复,并且出现的次数应该匹配。我会更新我的问题。
  • 在这种情况下,您需要计算和比较计数。我认为 hasItems 不会起作用,如果@Jorn Vernee 正在谈论 hamcrest 匹配器,它将在第一场比赛后停止。
  • 我会试试这个,并进行必要的更改以使其适应 Java6,因为这是我正在使用的版本。
猜你喜欢
  • 2017-09-20
  • 1970-01-01
  • 1970-01-01
  • 2013-01-16
  • 2022-01-09
  • 2020-10-21
  • 2021-09-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多