代码之家  ›  专栏  ›  技术社区  ›  tkruse

转换Java流并返回减少的值

  •  0
  • tkruse  · 技术社区  · 7 年前

    假设我从一个我不想具体化的源中使用一个实体流,并且我想转换元素,并返回一些全局减少的值,那么java(8)的惯用方法是什么?

    这本质上是试图同时执行 reduce() 还有 collect() .

    例子:

    class Person {
        public String firstname,
        public String lastname,
        public int age;
    }
    
    class TeamSummary {
        public List<String> fullnames, // firstname and lastname of all
        public Person oldest
    }
    
    public TeamSummary getSummary(Stream<Person> personStream) {
       final TeamSummary summary = new Summary();
       summary.fullnames = personStream
           .peek(p -> if (summary.getOldest() == null || summary.getOldest.age < p.age) {
               summary.oldest = p;
           })
           .map(p -> p.firstname + ' ' + p.lastname)
           .collect(toList());
       return summary;
    }
    

    在peek方法中与流之外的变量交互看起来很难看,但是有什么好的替代方案,似乎我需要结合起来 收集 减少 .

    如果我想从整个数据流(比如平均年龄)和筛选列表(比如18岁以上的人)中得到一个减少的值,情况会变得更糟。如果TeamSummary是一个不可变的类,并且需要额外的可变变量,情况也会变得更糟。

    在这种情况下,在流中使用while循环更为惯用。迭代器()以避免流方法和变量的耦合?或者使用reduce-to-tuple(最古老的、累积的)是很自然的吗。

    我知道这个问题是一个意见问题,除非有一个明显的方法(比如特别的收藏家)优雅地解决这个问题。

    0 回复  |  直到 7 年前
        1
  •  4
  •   sfiss    7 年前

    所以你想把你的收藏减少到一个单一的价值?那就是 Collectors.reducing 发挥作用(备选方案:你可以使用 Stream.reduce 但经过其他修改)。此外,您希望以某种方式聚合您的值,并拥有完美的累加器: TeamSummary .

    现在,在下面的代码中,我做了愚蠢的调整:

    • Team Summary具有reduce所需的合并/标识功能,因为它充当累加器
    • 我用 Null Object 而不是 null 对于一个不存在的人来说,这使得代码在没有空检查的情况下更具可读性(转换期间的NPE是问题之一)。如果流为空,您是否考虑过输出?
    • 我加了一个 Person 为了我自己的方便。但是考虑使用getter和final字段(即使您认为getter和整个伪封装都是样板:您可以使用方法引用,例如,传递给比较器,但不能使用字段引用)

    以下是代码:

    static class Person {
        public String firstname;
        public String lastname;
        public int age;
    
        public Person(String firstname, String lastname, int age) {
            this.firstname = firstname;
            this.lastname = lastname;
            this.age = age;
        }
    
        public static Person getNullObjectYoung() {
            return new Person("", "", 0);
        }
    }
    
    static class TeamSummary {
        public List<String> fullnames;
        public Person oldest;
    
        public static TeamSummary merge(TeamSummary lhs, TeamSummary rhs) {
            TeamSummary result = new TeamSummary();
            result.fullnames = new ArrayList<>();
            result.fullnames.addAll(lhs.fullnames);
            result.fullnames.addAll(rhs.fullnames);
            result.oldest = Comparator.<Person, Integer>comparing(p -> p.age).reversed()
                    .compare(lhs.oldest, rhs.oldest) < 0
                    ? lhs.oldest
                    : rhs.oldest;
            return result;
        }
    
        public static TeamSummary of(Person person) {
            TeamSummary result = new TeamSummary();
            result.fullnames = new ArrayList<>();
            result.fullnames.add(person.firstname + " " + person.lastname);
            result.oldest = person;
            return result;
        }
    
        public static TeamSummary identity() {
            TeamSummary result = new TeamSummary();
            result.fullnames = new ArrayList<>();
            result.oldest = Person.getNullObjectYoung();
            return result;
        }
    }
    
    public static void main(String[] args) {        
        Stream<Person> personStream = Arrays.asList(
                new Person("Tom", "T", 32),
                new Person("Bob", "B", 40))
                .stream();
    
        TeamSummary result = personStream.collect(
                Collectors.reducing(
                        TeamSummary.identity(),
                        TeamSummary::of,
                        TeamSummary::merge
                ));
        System.out.println(result.fullnames + " " + result.oldest.age);
    
    }
    

    注意:您要求的是java 8版本。也许在Java12中,您也可以使用 Collectors.teeing ,因为你基本上想同时做两个不同的减少(我们目前可以利用累加器)。


    编辑:还添加了一个解决方案 流动减少 ,它需要一个双功能(摘要、人员)——>人:

    static class TeamSummary {
    
        ...
    
        public TeamSummary include(final Person person) {
            final TeamSummary result = new TeamSummary();
            result.fullnames = new ArrayList<>(fullnames);
            result.fullnames.add(person.firstname + " " + person.lastname);
            result.oldest = Comparator.<Person, Integer> comparing(p -> p.age).reversed()
                    .compare(oldest, person) < 0
                            ? oldest
                            : person;
            return result;
        }
    }
    
    public static void main(final String[] args) {
        ...
    
        final TeamSummary reduced = personStream.reduce(
                TeamSummary.identity(),
                TeamSummary::include,
                TeamSummary::merge);
    }
    
        2
  •  1
  •   Naman    7 年前

    基于以下要求:- Stream 作为输入,并在输出中推断出完整的姓名列表 teamSummary .你可以做这个手术 map 将此人及其姓名详细信息ping到条目,然后 reduce 进一步包括:

    return personStream
            .map(p -> new AbstractMap.SimpleEntry<>(p, Collections.singletonList(p.getFirstname() + ' ' + p.getLastname())))
            .reduce((entry1, entry2) -> new AbstractMap.SimpleEntry<>(entry1.getKey().getAge() >= entry2.getKey().getAge() ?
                    entry1.getKey() : entry2.getKey(), Stream.of(entry1.getValue(), entry2.getValue()).flatMap(List::stream).collect(Collectors.toList())))
            .map(entry -> new TeamSummary(entry.getKey(), entry.getValue()))
            .orElseThrow(IllegalArgumentException::new);
    

    为了便于阅读和简化,我建议在这里传递集合并使用多个流操作来构建 TeamSummary 作为:

    public TeamSummary getSummary(List<Person> people) {
        List<String> fullNames = people.stream()
                .map(p -> p.getFirstname() + ' ' + p.getLastname())
                .collect(Collectors.toList());
        Person oldestPerson = people.stream()
                .reduce(BinaryOperator.maxBy(Comparator.comparing(Person::getAge)))
                .orElseThrow(IllegalArgumentException::new);
        return new TeamSummary(oldestPerson, fullNames);
    }
    
        3
  •  0
  •   daniu    7 年前

    我不知道你为什么要用 Collectors.reducing() 当你可以的时候 stream.reduce() 直接地

    BinaryOperator<Player> older = (p1, p2) -> 
        Comparator.comparing(Player::getAge) > 0 
            ? p1 : p2;
    TeamSummary summary = stream.reduce(
        TeamSummary::new, // identity
        // accumulator
        (ts, player) -> {
            ts.addFullnames(String.format("%s %s", player.firstName, player.lastName));
            ts.setOldest(older.apply(ts.getOldest(), player));
        }
        // combiner
        (ts1, ts2) -> {
            // we can safely modify the given summaries, they were all created while reducing
            ts1.setOldest(Math.max(ts1.getOldest(), ts2.getOldest()));
            ts1.addFullnames(ts2.getFullnames().toArray());
            return ts1;
        });
    

    TeamSummary 然后看起来是这样的:

    class TeamSummary {
        private int oldest; 
        public Player getOldest() { return oldest; }
        public void setOldest(Player newOldest) { oldest = newOldest; }
    
        private List<String> fullnames();
        public List<String> getFullnames() { return Collections.unmodifiableList(fullnames); }
    
        public void addFullnames(String... names) {
            fullnames.addAll(Arrays.asList(names));
        }
    }
    

    可供替代的

    你也可以延长 团队总结 比如 addPlayer(Player p) merge() 为了使其保持一致性:

    class TeamSummary {
    
        @Getter
        private int oldest;
        @Getter
        private List<String> fullnames = new ArrayList<>();
    
        public void addPlayer(Player p) {
            fullnames.add(String.format("%s %s", p.getFirstname(), p.getLastname()));
            oldest = olderPlayer(oldest, p);
        }
        public TeamSummary merge(TeamSummary other) {
            older = olderPlayer(oldest, other.oldest)
            fullnames.addAll(other.fullnames);
            return this;
        }
    
        final static Comparator<Player> BY_AGE = Comparator.comparing(Player::getAge);
        private static Player olderPlayer(Player p1, Player p2) {
            return BY_AGE.compare(p1, p2) > 0 ? p1 : p2;
        }
    }
    

    这将使减少

    stream.reduce(
        TeamSummary::new,
        TeamSummary::addPlayer,
        TeamSummary::merge
    );