Hamcrest 匹配器在字符串列表中没有重复项?

Hamcrest Matcher for no duplicates in a List of Strings?

我想要一个简单的 hamcrest 匹配器,用于在 List<String> 中查找对象的重复项。这是我写的

for (QuizEntity quiz : quizzes)
            for (QuestionEntity question : quiz.getQuestions())
                Assert.assertThat("There should be no duplicate questions", 1, Matchers.equalTo(Collections.frequency(questions, question.getQuestion())));

不幸的是,我得到了这个描述性不够的输出。任意

java.lang.AssertionError: There should be no duplicate questions
Expected: <20>
     but: was <1>

已替换

Assert.assertThat("There should be no duplicate questions", 1, Matchers.equalTo(Collections.frequency(questions, question.getQuestion())));

Assert.assertThat("Question '" + question.getQuestion() + "' should not be duplicated", 1, Matchers.equalTo(Collections.frequency(questions, question.getQuestion())));

另一种选择是使用 hasDistinctElements() matcher from Cirneco library(最后是 Hamcrest 扩展)。

可以这样使用:

Assert.assertThat("There should be no duplicate questions", questions, CirnecoMatchersJ7.hasDistinctElements());

虽然我建议这样做

import static CirnecoMatchersJ7.*;

使其更易于阅读

assertThat("There should be no duplicate questions", questions, hasDistinctElements());

Cirneco 也有一些流畅的表示,即

given(questions).withReason("There should be no duplicate questions").assertThat(hasDistinctElements());

可以按如下方式导入依赖:

<dependency>
  <groupId>it.ozimov</groupId>
  <artifactId>java7-hamcrest-matchers</artifactId>
  <version>0.7.0</version>
</dependency>

实际上有一个实现的方法可以做到这一点 - doesNotHaveDuplicates

 Assertions.assertThat(list).doesNotHaveDuplicates();

您可以参加测验并使用 Java 8 将其映射到问题并断言没有重复项

我为此实现了自己的匹配器。请注意错误消息对我来说没有正常工作(显示的是另一个条目而不是重复的条目)。我与社区共享代码(如果您想在您的产品中使用,请 link 和投票)

密码

public static class DistinctMatcher<T> extends BaseMatcher<T>
{
    private final Set<T> theSet = new HashSet<>();

    public static <T> Matcher<? super T> distinct()
    {
        return new DistinctMatcher<T>();
    }

    @Override
    public void describeTo(Description description)
    {
        description.appendText("has distinct values");
    }

    @Override
    public void describeMismatch(Object item, Description description)
    {
        description.appendText("element found twice: ").appendValue(item);
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object arg0)
    {
        return theSet.add((T) arg0);
    }

}

工作原理

通过维护有状态 HashSet,匹配器被实例化一次并由迭代的每个项目提供。根据Set.add方法,它会告诉你集合是否已经包含该项目,所以匹配是立即的。

用法:(我在非嵌套集合上测试过)

assertThat(quizzes,(everyItem(hasProperty("questions",everyItem(hasProperty("question",is(distinct())))))));

性能考虑

上面的匹配器不适合不受约束的惰性集合(例如数十亿条记录的迭代器),因为 Set 持有对该对象的永久引用。可以很容易地将匹配器修改为仅拥有对象的 hashCode,但应牢记内存要求。

任何其他基于集合的解决方案都会遇到同样的问题