java 对流数据进行分区并获得两个不同的集合作为结果

goucqfw6  于 2022-10-30  发布在  Java
关注(0)|答案(6)|浏览(123)

我有一个ID列表myIdList和一个MapmyObjectMap,它将ID与对应的对象Map<String,Object>相关联。
我需要检查我的ID列表,对于每个ID --如果它存在于Map中,则将与它关联的对象添加到新的对象列表中,否则将该ID添加到缺失ID的列表中。
我可以使用一个简单的for each循环轻松地完成这一任务:

List<String> myIdList = Arrays.asList("a", "b", "c");
Map<String,Object> myObjectMap = new HashMap<>();

List<Object> objectList = new ArrayList<>();
List<String> missingObjIds = new ArrayList<>();

for(String id : myIdList) {
    Object obj = myObjectMap.get(id);
    if(obj == null) {
        missingObjIds.add(id);
    } else {
        objectList.add(obj);
    }
}

我想使用流API以一种不那么冗长的方式完成这项工作,但我不确定如何完成。
我尝试过对流进行分区,但得到的要么是一个包含2个ID列表的map(这需要再次遍历列表以检索Map的对象),要么是一个对象列表,其中所有缺失的对象都为空,而且无法获得缺失的ID。
我相信一定有办法的。有什么想法吗?

qni6mghb

qni6mghb1#

这不是很有效,但你可以做到。

List<String> myIdList = Arrays.asList("a", "b", "c");
Map<String,Object> myObjectMap = new HashMap<>();
myObjectMap.put("b", "B");

Map<Boolean, List<String>> partitioned = myIdList.stream()
    .collect(Collectors.partitioningBy(myObjectMap::containsKey));

List<Object> objectList = partitioned.get(true).stream()
    .map(myObjectMap::get).collect(Collectors.toList());
List<String> missingObjIds = partitioned.get(false);

System.out.println("objectList=" + objectList);
System.out.println("missingObjIds=" + missingObjIds);

输出:

objectList=[B]
missingObjIds=[a, c]
3pmvbmvn

3pmvbmvn2#

如果你真的想使用流,这里有一种方法。

  • 这基于ID的存在创建了条目。
  • 然后,它按关键字“Missing”和“Present”对这些独立条目进行分组

数据

List<String> myIdList = Arrays.asList("a", "b", "c");
 Map<String, Object> myObjectMap = new HashMap<>();
 myObjectMap.put("c", "C");

该方法

Map<String, List<Object>> map = myIdList.stream()
         .map(id-> {
             Entry<String, Object> entry;
             Object obj = myObjectMap.get(id);
             if (obj == null) {
                 entry = new SimpleEntry<>("Missing", id);
             } else {
                 entry = new SimpleEntry<>("Present", obj);
             }
             return entry;
         }).collect(Collectors.groupingBy(Entry::getKey, Collectors
                 .mapping(Entry::getValue, Collectors.toList())));

System.out.println(map.get("Present"));
System.out.println(map.get("Missing"));

印刷品

[C]
[a, b]

但我希望你坚持你所拥有的,这很有意义,也很有效。

ntjbwcob

ntjbwcob3#

这是一个foreach情况。

myIdList.stream().forEach(i -> {
    boolean b = myObjectMap.get(i) == null ? missingObjIds.add(i) : objectList.add(myObjectMap.get(i));
});
zbsbpyhn

zbsbpyhn4#

你可以在一个流中用分区/助手类来实现。但是,当2个流的实现是简洁的时候,为什么要使你的循环复杂呢?

List<Object> objectList    = myIdList.stream()
      .map(myObjectMap::get).filter(Objects::nonNull).toList();
List<String> missingObjIds = myIdList.stream()
      .filter(Predicate.not(myObjectMap::containsKey)).toList();
lymgl2op

lymgl2op5#

此逻辑可以在单个流语句中实现,并且通过使用自定义Collector(可以使用静态工厂方法Collector.of()创建)没有 * 副作用 *。

注意:我不会说这个解决方案比for-loop更简洁。但是它绝对是高性能可维护的,并且客户端代码简洁

为此,我们需要定义一个自定义的累积类型,它应该由一个能够使用流元素并生成最终结果的 mutable object 表示。
因为我们需要获得两个不同的列表,所以流执行的结果应该是公开这两个列表的累积类型本身。
因此,简单地说,custom type,让我们称之为Partitioner,我们将使用它来执行可变归约,它应该 Package 两个列表。如果我们使它成为泛型,并添加一个FunctionPredicate,这将涉及到将数据分区到它的属性,它将更加通用。
而且为了方便,我们可以实现Consumer接口。
这就是客户端代码中的stream-statement可能的样子:

public static void main(String[] args) {
    List<String> myIdList = Arrays.asList("a", "b", "c");
    Map<String, Object> myObjectMap = Map.of("a", "Alice");

    Partitioner<String, Object> result = myIdList.stream()
        .collect(partition(myObjectMap::containsKey, myObjectMap::get));

    System.out.println(result.getItems());
    System.out.println(result.getKeys());
}
  • 输出:*
[Alice] // Values assated with the Keys that match the IDs from the list
[b, c]  // missing IDs

生成Collector()示例的工厂方法partition()可能如下所示:

public static <K, R> Collector<K, ?, Partitioner<K, R>> partition(Predicate<K> predicate,
                                                                  Function<K, R> mapper) {
    return Collector.of(
        () -> new Partitioner<>(predicate, mapper),
        Partitioner::accept,
        Partitioner::merge
    );
}

这就是包含实际逻辑的 * 自定义累加类型 * 的实现方式:

public static class Partitioner<K, R> implements Consumer<K> {

    private List<R> items = new ArrayList<>(); // objectList
    private List<K> keys = new ArrayList<>();  // missingObjIds
    private Predicate<K> shouldStoreItem;
    private Function<K, R> mapper;

    public Partitioner(Predicate<K> shouldStoreItem, Function<K, R> mapper) {
        this.shouldStoreItem = shouldStoreItem;
        this.mapper = mapper;
    }

    @Override
    public void accept(K k) {
        if (shouldStoreItem.test(k)) items.add(mapper.apply(k));
        else keys.add(k);
    }

    public Partitioner<K, R> merge(Partitioner<K, R> other) {
        items.addAll(other.items);
        keys.addAll(other.keys);
        return this;
    }

    // getters
}
dm7nw8vv

dm7nw8vv6#

简单点!!!

List<String> ids = Arrays.asList("a", "b", "c");
Map<String, Object> map = Map.of();

List<Object> objectList = ids.stream()
                             .filter(map::containsKey)
                             .map(map::get)
                             .collect(Collectors.toList());
List<String> missingIds = ids.stream()
                             .filter(id -> !map.containsKey(id))
                             .collect(Collectors.toList());

相关问题