将Java-8流的结果分割成成功和失败列表。

问题描述 投票:0回答:1

我有一个List of Foo,在每个Foo上我应用一个处理器的方法来得到 ValidItem. 如果在处理过程中出现了错误,那么我返回了 ErrorItem.

现在如何处理这个由Java 8流得到的结果在2个不同的列表的形式

List<Foo> FooList = someList....;

class ValidItem extend Item{......}
class ErrorItem extend Item{......}


Item processItem(Foo  foo){
   return either an object of ValidItem or ErrorItem;
}

我相信我可以做到

 Map<Class,List<Item>> itemsMap =
    FooList
    .stream()
    .map(processItem)
    .collect(Collectors.groupingBy(Object::getClass));

但由于 List<Parent> 是不是一个 List<Child> 所以,我不能把地图结果打造成 List<ValidItem>在现实中 ErrorItemValidItem 是两个完全不同的类,完全没有关系,只是为了这个蒸汽处理和processItem方法,我通过扩展一个标记Item类,把它们保持在同一个层次结构中,在代码中的许多其他地方,我不能把ValidItem称为Item,因为它让人觉得它也可以是一个ErroItem。

在代码中的许多其他地方,我不应该把ValidItem称为Item,因为它让人觉得它也可以是一个ErroItem。

ErrorItem和ValidItem没有扩展同一个Item类?

正如我所说,ValidItem和ErrorItem不应该是一样的,所以我改变了过程方法的签名,并给它传递了一个列表。我知道这不是Stream应该使用的方式。如果你有更好的方法,请告诉我

    List<Foo> FooList = someList....;

    class ValidItem {......}
    class InvalidFoo{......}


    ValidItem processFoo(Foo  foo, List<InvalidFoo> foolist){
      Do some processing on foo.
       either return  new ValidItem ();
         OR 
         fooList.add(new InvalidFoo()) , and then return null;
    }

List<InvalidFoo> invalidFooList = new ArrayList();
     List<ValidItem> validItem =
        fooList
        .stream()
        .map(e->processItem(e,invalidFooList))
        .filter(Objects::notNull)
        .collect(Collectors.toList());

现在我有无效和有效的列表,但这看起来不像是一个干净的流代码。

java java-8 java-stream collectors
1个回答
5
投票

随着最近的Java版本,你可以使用

  • 对于 Item processItem(Foo foo) 方法返回的是 ValidItemErrorItem:

    Map.Entry<List<ValidItem>, List<ErrorItem>> collected = fooList.stream()
      .map(this::processItem)
      .collect(teeing(
        flatMapping(x -> x instanceof ValidItem? Stream.of((ValidItem)x):null, toList()),
        flatMapping(x -> x instanceof ErrorItem? Stream.of((ErrorItem)x):null, toList()),
        AbstractMap.SimpleImmutableEntry::new
      ));
    
    List<ValidItem> valid = collected.getKey();
    List<ErrorItem> invalid = collected.getValue();
    
  • 代表: ValidItem processFoo(Foo foo, List<InvalidFoo> foolist):

    Map.Entry<List<ValidItem>, List<InvalidFoo>> collected = fooList.stream()
        .map(foo -> {
            List<InvalidFoo> invalid = new ArrayList<>(1);
            ValidItem vi = processFoo(foo, invalid);
            return new AbstractMap.SimpleImmutableEntry<>(
                vi == null? Collections.<ValidItem>emptyList():
                            Collections.singletonList(vi),
                invalid);
        })
        .collect(teeing(
            flatMapping(e -> e.getKey().stream(), toList()),
            flatMapping(e -> e.getValue().stream(), toList()),
            AbstractMap.SimpleImmutableEntry::new
        ));
    
    List<ValidItem> valid = collected.getKey();
    List<InvalidFoo> invalid = collected.getValue();
    

负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: 负责人: flatMapping 在这个特定的情况下,在Java 9中引入了收集器,而不是用

flatMapping(x -> x instanceof ValidItem? Stream.of((ValidItem)x): null, toList())

您也可以使用

filtering(x -> x instanceof ValidItem, mapping(x -> (ValidItem)x, toList()))

但每个变体都需要Java 9,因为 filtering 在Java 8中也不存在。在Java 8中也不存在。teeing 收集器甚至需要Java 12。

然而,这些收集器并不难实现。

这个答案 包含一个Java 8兼容版本的 flatMapping 最后的收集器。如果你想用 filteringmapping,你可以找到一个Java 8兼容的版本。filtering本回答. 最后: 本回答 包含一个兼容Java 8的变体 teeing 收集器。

当你把这些收集器添加到你的代码库中时,本答案开头的解决方案就可以在Java 8下工作,并将很容易适应未来的版本。假设一个 import static java.util.stream.Collectors.*; 在你的源文件中,你只需要删除这些方法的后传,就可以切换到标准的JDK版本。


如果 processItem 返回一个 EitherPair 类型,而不是上面提到的两种变体。如果你不想使用第三方库,你可以使用一个叫做 Map.Entry 实例作为一个 "穷人的对子类型"。

有一个方法签名,比如

/** Returns an entry with either, key or value, being {@code null} */
Map.Entry<ValidItem,InvalidFoo> processItem(Foo foo){

可以通过返回一个 AbstractMap.SimpleImmutableEntry,

一个JDK 9+的解决方案可能是这样的

Map.Entry<List<ValidItem>, List<InvalidFoo>> collected = fooList.stream()
    .map(this::processItem)
    .collect(teeing(
        flatMapping(e -> Stream.ofNullable(e.getKey()), toList()),
        flatMapping(e -> Stream.ofNullable(e.getValue()), toList()),
        AbstractMap.SimpleImmutableEntry::new
    ));

和Java 8兼容(当使用链接的收集器后传)版本。

Map.Entry<List<ValidItem>, List<InvalidFoo>> collected = fooList.stream()
    .map(this::processItem)
    .collect(teeing(
        flatMapping(e -> Stream.of(e.getKey()).filter(Objects::nonNull), toList()),
        flatMapping(e -> Stream.of(e.getValue()).filter(Objects::nonNull), toList()),
        AbstractMap.SimpleImmutableEntry::new
    ));

Map.Entry<List<ValidItem>, List<InvalidFoo>> collected = fooList.stream()
    .map(this::processItem)
    .collect(teeing(
        mapping(Map.Entry::getKey, filtering(Objects::nonNull, toList())),
        mapping(Map.Entry::getValue, filtering(Objects::nonNull, toList())),
        AbstractMap.SimpleImmutableEntry::new
    ));

2
投票
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Foo> FooList = new ArrayList<>();
        for(int i = 0 ; i < 100; i++){
            FooList.add(new Foo(i+""));
        }
        Map<Class,List<Item>> itemsMap =
                FooList
                        .stream()
                        .map(Main::processItem)
                        .collect(Collectors.groupingBy(Object::getClass));
        List<ValidItem> validItems = itemsMap.get(ValidItem.class).stream().map((o -> (ValidItem)o)).collect(Collectors.toList());



    }
    public static Item processItem(Foo  foo){
        Random random = new Random(System.currentTimeMillis());
        if(Integer.parseInt(foo.name) % 2== 0){
            return new ValidItem(foo.name);
        }else{
            return new ErrorItem(foo.name);
        }
    }
    static class ValidItem extends Item{
        public ValidItem(String name) {
            super("valid: " + name);
        }
    }
    static class ErrorItem extends Item{
        public ErrorItem(String name) {
            super("error: "+name);
        }
    }
    public static class Item {
        private String name;

        public Item(String name) {
            this.name = name;
        }
    }

}

我建议这个解决方案。


0
投票

你可以使用Vavr库。

final List<String> list = Arrays.asList("1", ",", "1", "0");
final List<Either<ErrorItem, ValidItem>> eithers = list.stream()
                .map(MainClass::processData)
                .collect(Collectors.toList());
final List<ValidItem> validItems = eithers.stream()
                .filter(Either::isRight)
                .map(Either::get)
                .collect(Collectors.toList());
final List<ErrorItem> errorItems = eithers.stream()
                .filter(Either::isLeft)
                .map(Either::getLeft)
                .collect(Collectors.toList());

...

private static Either<ErrorItem,ValidItem> processData(String data){
        if(data.equals("1")){
            return Either.right(new ValidItem());
        }
        return Either.left(new ErrorItem());
}
© www.soinside.com 2019 - 2024. All rights reserved.