在很大程度上,作为一项学术活动,我正在试验我自己在Java中的Either
实现,受Haskell的either
(https://hackage.haskell.org/package/base-4.9.1.0/docs/Data-Either.html)启发
我有一个令我满意的工作实施:
Either<String, Integer> either = Either.left(A_STRING);
assertThat(either.left(), is(A_STRING));
assertThat(either.isLeft(), is(true));
assertThat(either.isRight(), is(false));
assertThat(either.map(x -> AN_INTEGER, y -> A_LONG), is(Either.left(AN_INTEGER)));
assertThat(either.consolidate(x -> A_LONG, y -> ANOTHER_LONG), is(A_LONG));
相当于Haskell的lefts
和rights
是过滤流:
Stream<String> s = streamOfEithers
.filter(Either::isLeft)
.map( x -> x.left())
我遇到的问题是创建一个等效的Haskell partitionEithers
。
我的第一直觉是:
streamOfEithers.collect(Collectors.groupingBy(Either::isLeft))
然而,这似乎丢失了我的类型 - 它返回Map<Boolean, Either<? extends Object, ? extends Object>>
而不是(例如)Map<Boolean, Either<String,Integer>
。
当然,理想情况下,分区的结果不是两个Eithers
列表,而是两个列表,每种类型一个。 Java没有Pair<T,U>
类,但如果有,可能是:
List<Either<String,Integer>> listOfEithers = ...
Pair<List<String>,List<Integer>> partitioned = Either.partitionList(listOfEithers);
否则,我所管理的最好的是:
Map<Boolean, Either<String,Integer>> partitioned = Either.partitionList(listOfEithers);
...对于客户来说这不是一个非常方便的结构。
除了编写Pair
或使用某些馆藏库中的Pair
之外(Guava,顺便提一下,拒绝提供Pair
,因为它“弊大于利”,我可以最好地提供与Haskell的partitionEithers
答案 0 :(得分:2)
您可以为此创建自己的聚合类型,代替Pair
,专门为Either
量身定制,其中包含一个收集器。这是一个简化的实现:
class Aggregate<T, U> {
List<T> tList = new ArrayList<>();
List<U> uList = new ArrayList<>();
public void add(Either<T, U> e) {
if(e.isLeft()) {
tList.add(e.left());
} else {
uList.add(e.right());
}
}
public Aggregate<T, U> combine(Aggregate<T, U> other) {
tList.addAll(other.tList);
uList.addAll(other.uList);
return this; // could create a new Aggregate here instead...
}
public static <T, U> Collector<Either<T,U>, ? , Aggregate<T, U>> partitionEithers() {
return Collector.of(Aggregate::new, Aggregate::add, Aggregate::combine);
}
}
然后结果变为:
Aggregate<String, Integer> result = streamOfEithers.collect(Aggregate.partitionEithers());
答案 1 :(得分:1)
试试这个:
Map<Boolean, List<Either<Integer, String>>> collect = list.stream().collect(Collectors.partitioningBy(Either::isLeft));
假设你的Either实现的泛型类型签名是Either<L,R>
(这是我用示例实现测试的),类型擦除应该没有问题,它将按预期编译。
之后,您可以像这样初始化Pair
:
Pair<List<Integer>, List<String>> result =
pair(
collect.getOrDefault(true, Collections.emptyList()).stream().map(Either::left).collect(toList()),
collect.getOrDefault(false, Collections.emptyList()).stream().map(Either::right).collect(toList()));
答案 2 :(得分:0)