我在这个函数式编程和流程中有点绿色,但我知道的一点点非常有用!
我已经多次出现过这种情况:
List<SomeProperty> distinctProperties = someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.toList());
if (distinctProperties.size() == 1) {
SomeProperty commonProperty = distinctProperties.get(0);
// take some action knowing that all share this common property
}
我真正想要的是:
Optional<SomeProperty> universalCommonProperty = someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.singleOrEmpty());
我认为singleOrEmpty
除了与distinct
结合使用外,在其他情况下也很有用。当我是一个超级n00b时,我花了很多时间重新发明Java Collections Framework,因为我不知道它在那里,所以我试图不重复我的错误。 Java是否有一个很好的方法来执行此singleOrEmpty
事情?我错误地制定了它吗?
谢谢!
编辑:这是distinct
案例的一些示例数据。如果您忽略map
步骤:
Optional<SomeProperty> universalCommonProperty = someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.singleOrEmpty());
[] -> Optional.empty()
[1] -> Optional.of(1)
[1, 1] -> Optional.of(1)
[2, 2] -> Optional.of(2)
[1, 2] -> Optional.empty()
当我搞砸了我的类型或遗留代码时,我发现我需要这个。很高兴能够快速说出#34;这个集合的所有元素都共享这个属性,所以现在我可以使用这个共享属性采取一些行动。&#34;另一个例子是当用户多选一些不同的元素时,你会试图看看你能做什么(如果有的话)对所有这些元素都有效。
EDIT2:对不起,如果我的例子有误导性。关键是 singleOrEmpty 。我经常发现我在前面放了一个distinct
,但它可以很容易地成为其他类型的filter
。
Optional<SomeProperty> loneSpecialItem = someList.stream()
.filter(obj -> obj.isSpecial())
.collect(Collectors.singleOrEmpty());
[special] -> Optional.of(special)
[special, special] -> Optional.empty()
[not] -> Optional.empty()
[not, special] -> Optional.of(special)
[not, special, not] -> Optional.of(special)
EDIT3:我认为我通过激励 singleOrEmpty 搞砸了,而不仅仅是单独要求它。
Optional<Int> value = someList.stream().collect(Collectors.singleOrEmpty())
[] -> Optional.empty()
[1] -> Optional.of(1)
[1, 1] -> Optional.empty()
答案 0 :(得分:19)
这将产生创建集合的开销,但它很简单并且即使您首先忘记distinct()流也能正常工作。
static<T> Collector<T,?,Optional<T>> singleOrEmpty() {
return Collectors.collectingAndThen(
Collectors.toSet(),
set -> set.size() == 1
? set.stream().findAny()
: Optional.empty()
);
}
答案 1 :(得分:13)
“Hacky”解决方案仅评估前两个元素:
.limit(2)
.map(Optional::ofNullable)
.reduce(Optional.empty(),
(a, b) -> a.isPresent() ^ b.isPresent() ? b : Optional.empty());
一些基本的解释:
单个元素 [1] - &gt;映射到[Optional(1)] - &gt;减少
"Empty XOR Present" yields Optional(1)
=可选(1)
两个元素 [1,2] - &gt;映射到[Optional(1),Optional(2)] - &gt;减少做:
"Empty XOR Present" yields Optional(1)
"Optional(1) XOR Optional(2)" yields Optional.Empty
= Optional.Empty
这是完整的测试用例:
public static <T> Optional<T> singleOrEmpty(Stream<T> stream) {
return stream.limit(2)
.map(Optional::ofNullable)
.reduce(Optional.empty(),
(a, b) -> a.isPresent() ^ b.isPresent() ? b : Optional.empty());
}
@Test
public void test() {
testCase(Optional.empty());
testCase(Optional.of(1), 1);
testCase(Optional.empty(), 1, 1);
testCase(Optional.empty(), 1, 1, 1);
}
private void testCase(Optional<Integer> expected, Integer... values) {
Assert.assertEquals(expected, singleOrEmpty(Arrays.stream(values)));
}
向Ned(OP)致敬,他们提供了XOR理念和上述测试用例!
答案 2 :(得分:7)
如果您不介意使用Guava,则可以使用Iterables.getOnlyElement
包装代码,因此它看起来像这样:
SomeProperty distinctProperty = Iterables.getOnlyElement(
someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.toList()));
如果有多个值或没有值,则会引发 IllegalArgumentException
,还有version默认值。
答案 3 :(得分:6)
为此构建收集器的更简洁方法如下:
Collectors.reducing((a, b) -> null);
reduce collector将存储第一个值,然后在连续传递时,将当前运行值和新值传递给lambda表达式。此时,可以始终返回null,因为不会使用第一个值调用null,而第一个值将被存储。
将其插入代码:
Optional<SomeProperty> universalCommonProperty = someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.reducing((a, b) -> null));
答案 4 :(得分:4)
您可以轻松编写自己的Collector
public class AllOrNothing<T> implements Collector<T, Set<T>, Optional<T>>{
@Override
public Supplier<Set<T>> supplier() {
return () -> new HashSet<>();
}
@Override
public BinaryOperator<Set<T>> combiner() {
return (set1, set2)-> {
set1.addAll(set2);
return set1;
};
}
@Override
public Function<Set<T>, Optional<T>> finisher() {
return (set) -> {
if(set.size() ==1){
return Optional.of(set.iterator().next());
}
return Optional.empty();
};
}
@Override
public Set<java.util.stream.Collector.Characteristics> characteristics() {
return Collections.emptySet();
}
@Override
public BiConsumer<Set<T>, T> accumulator() {
return Set::add;
}
}
您可以这样使用:
Optional<T> result = myStream.collect( new AllOrNothing<>());
这是您的示例测试数据
public static void main(String[] args) {
System.out.println(run());
System.out.println(run(1));
System.out.println(run(1,1));
System.out.println(run(2,2));
System.out.println(run(1,2));
}
private static Optional<Integer> run(Integer...ints){
List<Integer> asList = Arrays.asList(ints);
System.out.println(asList);
return asList
.stream()
.collect(new AllOrNothing<>());
}
运行时会打印出来
[]
Optional.empty
[1]
Optional[1]
[1, 1]
Optional[1]
[2, 2]
Optional[2]
答案 5 :(得分:3)
似乎RxJava具有类似的功能in its single()
operator。
single( )
和singleOrDefault( )
如果
Observable
在发出单个项目后完成,则返回该项目,否则抛出异常(或返回默认项目)
我宁愿拥有Optional
,而我更应该是Collector
。
答案 6 :(得分:1)
另一种收藏家方法:
收藏家:
var file = jQuery('#brandImageUpload').prop("files")[0];
window.URL = window.URL || window.webkitURL ;
var blobURL = window.URL.createObjectURL(file);
$('#ibrandImage').attr('src', blobURL);
$('#brandImage').slideDown();
$(this).slideUp();
SingleCollectorBase:
public final class SingleCollector<T> extends SingleCollectorBase<T> {
@Override
public Function<Single<T>, T> finisher() {
return a -> a.getItem();
}
}
public final class SingleOrNullCollector<T> extends SingleCollectorBase<T> {
@Override
public Function<Single<T>, T> finisher() {
return a -> a.getItemOrNull();
}
}
单
public abstract class SingleCollectorBase<T> implements Collector<T, Single<T>, T> {
@Override
public Supplier<Single<T>> supplier() {
return () -> new Single<>();
}
@Override
public BiConsumer<Single<T>, T> accumulator() {
return (list, item) -> list.set(item);
}
@Override
public BinaryOperator<Single<T>> combiner() {
return (s1, s2) -> {
s1.set(s2);
return s1;
};
}
@Override
public Set<Characteristics> characteristics() {
return EnumSet.of(Characteristics.UNORDERED);
}
}
测试和示例用法,尽管缺乏并行测试。
public final class Single<T> {
private T item;
private boolean set;
public void set(T item) {
if (set) throw new SingleException("More than one item in collection");
this.item = item;
set = true;
}
public T getItem() {
if (!set) throw new SingleException("No item in collection");
return item;
}
public void set(Single<T> other) {
if (!other.set) return;
set(other.item);
}
public T getItemOrNull() {
return set ? item : null;
}
}
public class SingleException extends RuntimeException {
public SingleException(String message) {
super(message);
}
}
答案 7 :(得分:0)
Guava有一个名为MoreCollectors.toOptional()
的收藏家