Java 8收集器,如果存在';只有一个值
我对函数编程和流之类的东西有点生疏,但我所知道的一点非常有用 我已经多次遇到这种情况:Java 8收集器,如果存在';只有一个值,java,java-8,java-stream,Java,Java 8,Java Stream,我对函数编程和流之类的东西有点生疏,但我所知道的一点非常有用 我已经多次遇到这种情况: List<SomeProperty> distinctProperties = someList.stream() .map(obj -> obj.getSomeProperty()) .distinct() .collect(Collectors.toList()); if (distinctProperties.size() == 1) { SomePr
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
}
当我搞砸了我的类型,或者有遗留代码时,我发现我需要这个。能够很快地说“这个集合的所有元素都共享这个属性,所以现在我可以使用这个共享属性采取一些行动”,这真是太好了。另一个例子是,当用户多重选择一些不同的元素时,您尝试查看您可以做哪些事情(如果有的话)对所有元素都有效
EDIT2:如果我的例子有误导性的话,对不起。键为单键或空键。我经常发现我在前面放了一个不同的,但它也可以很容易地成为另一种过滤器
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)
可选LonSpecialItem=someList.stream()
.filter(obj->obj.isSpecial())
.collect(collector.singleOrEmpty());
[特殊]->可选。of(特殊)
[特殊,特殊]->可选。空()
[not]->可选。空()
[非,特殊]->可选。of(特殊)
[非,特殊,非]->可选。of(特殊)
EDIT3:我想我把事情搞砸了,因为我激励了单身者,而不是自己要求
Optional<Int> value = someList.stream().collect(Collectors.singleOrEmpty())
[] -> Optional.empty()
[1] -> Optional.of(1)
[1, 1] -> Optional.empty()
Optional value=someList.stream().collect(Collectors.singleOrEmpty())
[]->Optional.empty()
[1] ->可选。共(1)
[1,1]->可选的.empty()
如果您不介意使用,您可以使用Iterables.getOnlyElement
包装您的代码,这样看起来就像:
SomeProperty distinctProperty = Iterables.getOnlyElement(
someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.toList()));
IllegalArgumentException
如果有多个值或没有值,则会引发,还有一个值带有默认值。您可以轻松编写自己的收集器
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;
}
}
这将导致创建集合的开销,但它很简单,即使您忘记首先对流进行distinct(),也可以正常工作
static<T> Collector<T,?,Optional<T>> singleOrEmpty() {
return Collectors.collectingAndThen(
Collectors.toSet(),
set -> set.size() == 1
? set.stream().findAny()
: Optional.empty()
);
}
静态收集器singleOrEmpty(){
返回收集器。正在收集,然后返回(
Collectors.toSet(),
set->set.size()==1
?set.stream().findAny()
:可选。空()
);
}
仅评估前两个元素的“黑客”解决方案:
.limit(2)
.map(Optional::ofNullable)
.reduce(Optional.empty(),
(a, b) -> a.isPresent() ^ b.isPresent() ? b : Optional.empty());
一些基本解释:
单个元素[1]->映射到[可选(1)]->减少
"Empty XOR Present" yields Optional(1)
=可选(1)
两个元素[1,2]->映射到[可选(1),可选(2)]->reduce不:
"Empty XOR Present" yields Optional(1)
"Optional(1) XOR Optional(2)" yields 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)));
}
public静态可选singleOrEmpty(Stream){
返回流。限制(2)
.map(可选::of nullable)
.reduce(可选的.empty(),
(a,b)->a.isPresent()^b.isPresent()?b:可选的.empty());
}
@试验
公开无效测试(){
testCase(可选的.empty());
测试用例(可选,共(1),1);
testCase(可选的.empty(),1,1);
testCase(可选的.empty(),1,1,1);
}
私有void测试用例(可选,预期为整型…值){
Assert.assertEquals(应为singleOrEmpty(Arrays.stream(values));
}
感谢Ned(OP),他贡献了XOR思想和上面的测试用例 似乎RxJava也有类似的功能
单一( )代码>和singleOrDefault( )代码>
如果可观察的在发出单个项后完成,则返回该项,否则抛出异常(或返回默认项)
我宁愿选择一个可选的
,我宁愿它是一个收集器
另一种收集器方法:
收藏家:
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();
}
}
公共最终类SingleCollector扩展了SingleCollectorBase{
@凌驾
公共函数完成器(){
返回a->a.getItem();
}
}
公共最终类SingleOrNullCollector扩展了SingleCollectorBase{
@凌驾
公共函数完成器(){
返回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);
}
}
公共抽象类SingleCollectorBase实现收集器{
@凌驾
公共供应商(){
return()->newsingle();
}
@凌驾
公共双消费者累加器(){
返回(列表,项目)->list.set(项目);
}
@凌驾
公共二进制运算符组合器(){
返回(s1、s2)->{
s1.集合(s2);
返回s1;
};
}
@凌驾
公共集特征(){
返回枚举集(特征无序);
}
}
单一:
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);
}
}
公共最终课程单{
私人物品;
私有布尔集;
公共无效集(T项){
如果(设置)抛出新的SingleException(“集合中的多个项”);
this.item=项目;
设置=真;
}
公共T getItem(){
如果(!set)抛出新的SingleException(“集合中没有项”);
退货项目;
}
公共无效集(单个其他){
如果(!other.set)返回;
成套设备(其他项目);
}
公共T getItemOrNull(){
返回集?项目:空;
}
}
公共类SingleException扩展了RuntimeException{
公共单条异常(字符串消息){
超级(信息);
}
}
测试和示例用法,尽管缺乏并行测试
public final class SingleTests {
@Test
public void collect_single() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleCollector<>());
}
@Test(expected = SingleException.class)
public void collect_no_entries() {
ArrayList<String> list = new ArrayList<>();
list.stream().collect(new SingleCollector<>());
}
@Test
public void collect_single_or_null() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleOrNullCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries_or_null() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleOrNullCollector<>());
}
@Test
public void collect_no_entries_or_null() {
ArrayList<String> list = new ArrayList<>();
assertNull(list.stream().collect(new SingleOrNullCollector<>()));
}
}
public final class SingleTests{
@试验
公共无效集合单(){
ArrayList=新建ArrayList();
列表。添加(“ABC”);
String collect=list.stream().collect(新的SingleCollector());
资产质量(“ABC”,收款);
}
@测试(预期=SingleException.class)
公共作废收集多个条目(){
ArrayList=新建ArrayList();
列表。添加(“ABC”);
列表。添加(“ABCD”);
list.stream().collect(新的SingleCollector());
}
@测试(预期=SingleException.class)
公共作废收集\u否\u条目(){
ArrayList=新建ArrayList();
list.stream().collect(新的
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)));
}
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);
}
}
public final class SingleTests {
@Test
public void collect_single() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleCollector<>());
}
@Test(expected = SingleException.class)
public void collect_no_entries() {
ArrayList<String> list = new ArrayList<>();
list.stream().collect(new SingleCollector<>());
}
@Test
public void collect_single_or_null() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleOrNullCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries_or_null() {
ArrayList<String> list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleOrNullCollector<>());
}
@Test
public void collect_no_entries_or_null() {
ArrayList<String> list = new ArrayList<>();
assertNull(list.stream().collect(new SingleOrNullCollector<>()));
}
}
Collectors.reducing((a, b) -> null);
Optional<SomeProperty> universalCommonProperty = someList.stream()
.map(obj -> obj.getSomeProperty())
.distinct()
.collect(Collectors.reducing((a, b) -> null));