仅当具有相同键的对象在Java 8中具有相同值时才获取值

时间:2017-10-21 01:26:22

标签: java java-8 java-stream

使用Java stream api,我想只在具有相同键的原始对象具有相同值时才获取新对象集中的值,否则将值设置为 null 。同时,删除重复的对象。

Set<Listing>

class Listing {
  int key;
  int val;
}

例如,我有一个包含以下实例的集合:

listing{key = 1, val = a}
listing{key = 1, val = a}
listing{key = 2, val = b}
listing{key = 2, val = c}
listing{key = 3, val = d}

最后我想得到一个包含以下实例的集合:

listing{key = 1, val = a}
listing{key = 2, val = null}// because b != c as per above
listing{key = 3, val = d}

如何使用java stream api获得结果?

3 个答案:

答案 0 :(得分:0)

Haven没试过,但也许它会起作用。

listings.stream()
    .collect(Collector.of(
        HashMap::new,
        ( Map<Integer, Set<Integer>> map, Listing listing ) -> {
            map.computeIfAbsent(listing.key, i -> new HashSet<>()).add(listing.val);
        },
        ( Map<Integer, Set<Integer>> map1, Map<Integer, Set<Integer>> map2 ) -> {
            map2.forEach(( k, v ) -> map1.get(k).addAll(v));
            return map1;
        },
        map -> {
            Set<Listing> set = new HashSet<>();
            map.forEach(( Integer k, Set<Integer> v ) -> {
                Listing listing = new Listing();
                listing.key = k;
                if( v.size() == 1 ){
                    listing.val = v.iterator().next();
                } else{
                    listing.val = null;
                }
                set.add(listing);
            });
            return set;
        },
        Collector.Characteristics.UNORDERED)
    );

答案 1 :(得分:0)

首先,val是String,而不是int,对吗? 其次,您可能需要Map,而不是Set 以下是使用流和简单reduce函数的两种解决方案。 它没有准备好并行工作,但是你可以改进它。 :)

获得Map

Map<Integer, String> mapping = lising.stream()
    .reduce(
        new HashMap<Integer, String>(),
        (map, lst) -> {
            if(!map.containsKey(lst.key)) {
                map.put(lst.key, lst.val);
            } else if(!Objects.equals(map.get(lst.key), lst.val)) {
                map.put(lst.key, null);
            }
            return map;
        },
        (map1, map2) -> map1 // For parallel usage - TODO
    );

获得Set

Set<Listing> set = new HashSet<>(lising.stream()
    .reduce(
            new HashMap<Integer, Listing>(),
            (map, lst) -> {
                if(!map.containsKey(lst.key)) {
                    map.put(lst.key, lst.val);
                } else if(!Objects.equals(map.get(lst.key).val, lst.val)) {
                    map.put(lst.key, new Listing(lst.key, null));
                }
                return map;
            },
            (map1, map2) -> map1 // For parallel usage - TODO
    ).values());

请注意,这两个解决方案只会在初始列表中传输一次,因此非常有效。

答案 2 :(得分:0)

您说明key的类型为int,但是为其分配了字母,更糟糕的是,对于某些条件,结果应该是null

将班级更改为

class Listing {
    int key;
    String val;

    public Listing(int key, String val) {
        this.key = key;
        this.val = val;
    }

    @Override
    public String toString() {
        return "Listing{key=" + key + ", val=" + val + '}';
    }
}

您可以将任务实现为

// Example setup
Set<Listing> set = new LinkedHashSet<>();
set.add(new Listing(1, "a"));
set.add(new Listing(1, "a"));
set.add(new Listing(2, "b"));
set.add(new Listing(2, "c"));
set.add(new Listing(3, "d"));

// solution
ArrayList<Listing> result = set.stream()
    .collect(Collectors.collectingAndThen(
        Collectors.toMap(l -> l.key, l -> l,
            (l1,l2) -> Objects.equals(l1.val, l2.val)? l1: new Listing(l1.key, null),
            LinkedHashMap::new),
        m -> new ArrayList<>(m.values())));

// print as stated in your question
result.forEach(System.out::println);