断言 List<List<String>> 包含没有顺序的 List<String>

Assert List<List<String>> contains List<String> with no order

我有一个 List<List<String>>,样本数据如下:

("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<String>,但没有考虑顺序。

例如,以下列表 ("T1#R1", "T1#R1", "T1#R4", "T1#R3") 应被视为出现在 List<List<String>> 中,因为它包含与第一个列表相同的项目,但顺序不同。

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

我知道我可以通过编程方式执行此操作,但正在徘徊是否有 Matcher 例如可以提供帮助。

我见过这样的断言:

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

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

PS: 我正在使用 Java6, hamcrest-core-1.3, testng-5.14.1

我不知道有哪个 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")
例如,

会 return 为真。你有重复的吗? (对于 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 循环可能会在之前停止。

我不知道有任何匹配器可以满足您的要求,所以恐怕您必须对其进行编程。

我会简单地对目标列表进行排序,然后迭代子列表直到找到匹配项:

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:

逻辑和Java8版本完全一样。首先对目标列表进行排序,然后比较每个子列表,直到找到匹配项:

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);
}

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

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

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