Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何测试java-8自定义收集器的标识和关联性约束_Java_Java Stream - Fatal编程技术网

如何测试java-8自定义收集器的标识和关联性约束

如何测试java-8自定义收集器的标识和关联性约束,java,java-stream,Java,Java Stream,我已经为Java8编写了一个自定义收集器。其聚合器是一个包含一对列表的映射: @Override public Supplier<Map<Boolean, List<Object>>> supplier() { return () -> { Map<Boolean, List<Object>> map = new HashMap<>(2);

我已经为Java8编写了一个自定义收集器。其聚合器是一个包含一对列表的映射:

    @Override
    public Supplier<Map<Boolean, List<Object>>> supplier() {
        return () -> {
            Map<Boolean, List<Object>> map = new HashMap<>(2);
            map.put(false, new ArrayList<>());
            map.put(true, new ArrayList<>());
            return map;
        };
    }
@覆盖
公共供应商(){
返回()->{
Map Map=新的HashMap(2);
put(false,new ArrayList());
put(true,new ArrayList());
返回图;
};
}
所以我认为它的组合器是这样的:

    @Override
    public BinaryOperator<Map<Boolean, List<Object>>> combiner() {
        return (a, b) -> {
            a.get(false).addAll(b.get(false));
            a.get(true).addAll(b.get(true));
            return a;
        };
    }
@覆盖
公共二进制运算符组合器(){
返回(a,b)->{
a、 get(false).addAll(b.get(false));
a、 addAll(b.get(true));
返回a;
};
}
我想测试收集器,以确保当它并行处理流时,结果是正确的

我如何编写一个单元测试来测试这个问题

当然,我可以编写一个直接调用
组合器的测试,但这不是我想要的。我想要证据证明它在收集的过程中起作用

收集器的Javadoc说明:

为了确保顺序执行和并行执行产生等效的结果,收集器函数必须满足标识和关联性约束


通过测试这些约束条件,我是否可以获得对收集器的信心?怎么做?

你基本上是在问是否是关联的。因为标识是由(您使用的)每个标准集合保证遵循的

结合性 关联性是指以下内容:

在包含同一关联运算符行中两个或多个匹配项的表达式中,只要操作数序列不发生更改,执行操作的顺序就无关紧要。也就是说,重新排列此类表达式中的括号不会更改其值。考虑下列方程:

(2 + 3) + 4 = 2 + (3 + 4) = 9
2 × (3 × 4) = (2 × 3) × 4 = 24
--

列表。addAll
s是关联的。

让我们用例子来说明:

import java.util.*;
public class Main {
  // Give addAll an operator look.
  static <T> List<T> myAddAll(List<T> left, List<T> right) {
    List<T> result = new ArrayList<>(left);
    result.addAll(right);
    return result;
  }
  public static void main(String[] args) {
    List<Integer> a = Arrays.asList(1, 2, 3);
    List<Integer> b = Arrays.asList(4, 5, 6);
    List<Integer> c = Arrays.asList(7, 8, 9);

    // Combine a and b first, then combine the result with c.
    System.out.println(myAddAll(myAddAll(a, b), c)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

    // Combine b and c first, then combine a with the result.
    System.out.println(myAddAll(a, myAddAll(b, c))); // [1, 2, 3, 4, 5, 6, 7, 8, 9]
  }
}
import java.util.*;
公共班机{
//给addAll看一眼。
静态列表myAddAll(左列表、右列表){
列表结果=新建ArrayList(左);
结果:addAll(右);
返回结果;
}
公共静态void main(字符串[]args){
列表a=Arrays.asList(1,2,3);
listb=Arrays.asList(4,5,6);
listc=Arrays.asList(7,8,9);
//先把a和b结合起来,然后把结果和c结合起来。
System.out.println(myAddAll(myAddAll(a,b,c));//[1,2,3,4,5,6,7,8,9]
//先把b和c组合起来,然后把a和结果组合起来。
System.out.println(myAddAll(a,myAddAll(b,c));//[1,2,3,4,5,6,7,8,9]
}
}
测试
Collector
的契约正是您编写的:确保组合器同时具有identity和associativity属性。如果你遵循这一点,你就不会有任何问题(当然,如果需要的话,一定要暗示你的想法)


然后,测试归结为简单测试组合器是否具有这两个属性。标识部分由equals保证,关联部分由编写类似于上述代码的测试来处理。归根结底,这是因为,@mrmcgreg在评论中说,您不应该测试框架本身:这是Java作者的责任。如果在证明组合器满足这两个属性后遇到任何问题,您可能应该向Java提交一个bug。

Olivier回答了关联性/标识部分

关于测试,您可以编写自己的测试用例,希望能够涵盖所有角落的测试用例,或者尝试基于属性的测试ala Haskell(例如Java)


这将生成一组随机对象,并查看在应用运算符时声明的属性是否适用于所有对象。要进入一个更陡峭的学习曲线,但之后值得付出努力:)

感谢两位回答者,他们让我走上了我认为正确的道路

当然,也可以创建一个并行流,以将
收集器作为一个整体来运行:

T result = myList.stream().parallel().collect(myCollector);
但你不能保证它会分裂的边界,甚至不能保证它会分裂;除了编写自定义的
拆分器

因此,测试合同似乎是一条路要走。信任
Stream.collect()
采集器正常工作的情况下做正确的事情。通常不测试“提供的”库


Collector
JavaDoc定义了约束,甚至提供了描述关联性约束的代码。我们可以将此代码拉入一个在现实世界中可用的测试类:

public class CollectorTester<T, A, R> {

    private final Supplier<A> supplier;
    private final BiConsumer<A, T> accumulator;
    private final Function<A, R> finisher;
    private final BinaryOperator<A> combiner;

    public CollectorTester(Collector<T, A, R> collector) {
        this.supplier = collector.supplier();
        this.accumulator = collector.accumulator();
        this.combiner = collector.combiner();
        this.finisher = collector.finisher();
    }

    // Tests that an accumulator resulting from the inputs supplied
    // meets the identity constraint
    public void testIdentity(T... ts) {
        A a = supplier.get();
        Arrays.stream(ts).filter(t -> t != null).forEach(
            t -> accumulator.accept(a, t)
        );

        assertThat(combiner.apply(a, supplier.get()), equalTo(a));
    }

    // Tests that the combiner meets the associativity constraint
    // for the two inputs supplied
    // (This is verbatim from the Collector JavaDoc)
    // This test might be too strict for UNORDERED collectors
    public void testAssociativity(T t1, T t2) {
        A a1 = supplier.get();
        accumulator.accept(a1, t1);
        accumulator.accept(a1, t2);
        R r1 = finisher.apply(a1); // result without splitting

        A a2 = supplier.get();
        accumulator.accept(a2, t1);
        A a3 = supplier.get();
        accumulator.accept(a3, t2);
        R r2 = finisher.apply(combiner.apply(a2, a3)); // result with splitting

        assertThat(r1, equalTo(r2));
    }

}
(我感到高兴的是,我的测试代码不需要知道
收集器的类型。joining()
的累加器(API未声明)才能运行此测试)



注意,这只测试关联性和标识约束——您还需要测试收集器的域逻辑。通过检查
collect()
的结果和直接调用
Collector
的方法来实现这一点可能是最安全的。

实际上取决于
流。由
拆分器
决定如何对不同线程的数据进行分割。因此,如果您实现了一个
拆分器
,该拆分器具有拆分数据的
trySplit
,那么您可以并行地“强制”执行。但是请注意,几乎不可能通过测试来验证并行代码的正确功能。另一方面,您的收集器与@Boristespider有何不同?@Boristespider非常相似,但是输入类型是
,它被收集到一个包含
列表和
列表的类型中。不知道基本收集器是如何测试的。Java本身的测试代码在哪里?据我所知,累加器在哪里
@RunWith(Theories.class)
public class MaxCollectorTest {

    private final Collector<CharSequence, ?, String> coll = Collectors.joining();
    private final CollectorTester<CharSequence, ?, String> tester = new CollectorTester<>(coll);

    @DataPoints
    public static String[] datapoints() {
        return new String[] { null, "A", "rose", "by", "any", "other", "name" };
    }

    @Theory
    public void testAssociativity(String t1, String t2) {
        assumeThat(t1, notNullValue());
        assumeThat(t2, notNullValue());
        tester.testAssociativity(t1, t2);
    }

    @Theory
    public void testIdentity(String t1, String t2, String t3) {
        tester.testIdentity(t1, t2, t2);
    }
}