Java 8按条件分组求和

4
我有一个叫Officer的对象。
public class Officer {

    private String name;
    private int totalDaysInOffice;

    public Officer(String name, int totalDaysInOffice) {
        this.name = name;
        this.totalDaysInOffice = totalDaysInOffice;
    }

    @Override
    public String toString() {
        return "Officer{" +
                "name='" + name + '\'' +
                ", totalDaysInOffice=" + totalDaysInOffice +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getTotalDaysInOffice() {
        return totalDaysInOffice;
    }

    public void setTotalDaysInOffice(int totalDaysInOffice) {
        this.totalDaysInOffice = totalDaysInOffice;
    }
}

这里每个官员在办公室已经花费了若干天(只是一个虚构变量)。

我的目标是一旦我有了10000天的总和,将军官分成不同的列表

根据下面的示例,我想要一个包含以下内容的列表

one list with John , Matthew , and Robert since they sum to more 10K 
One list with Patrick as he has 10K 
Dave would be in separate list.

我已经尝试过使用group by,但不确定如何添加这个条件。
public class OffierExample {

    public static void main(String[] args) {

        List<Officer> officerList = new ArrayList<>();

        officerList.add(new Officer("John",5000));
        officerList.add(new Officer("Matthew",3000));
        officerList.add(new Officer("Robert",2000));
        officerList.add(new Officer("Dave",2000));
        officerList.add(new Officer("Patrick",10000));

        Map<Officer, Integer> collect = officerList.stream().collect(Collectors.groupingBy(o -> o, Collectors.summingInt(Officer::getTotalDaysInOffice)));
        System.out.println(collect);
    }
}

有没有办法在Java 8中完成此操作?

**

*****更新*****

**

我已经使用传统循环实现了,但如果可能的话,我想使用Java 8分组功能。

public class OffierExample {

    public static void main(String[] args) {

        List<Officer> officerList = new ArrayList<>();

        officerList.add(new Officer("John", 5000));
        officerList.add(new Officer("Matthew", 3000));
        officerList.add(new Officer("Robert", 2000));
        officerList.add(new Officer("Dave", 2000));
        officerList.add(new Officer("Patrick", 10000));

        officerList.add(new Officer("YYYY", 600));
        officerList.add(new Officer("XXXX", 600));

        //keep totalDaysInOfficeSum
        int totalDaysInOfficeSum = 0;

        //the final list
        List<List<Officer>> off = Lists.newArrayList();

        //the working list
        List<Officer> tempOffList = Lists.newArrayList();

        for (Officer officer : officerList) {

            //get sum
            totalDaysInOfficeSum = totalDaysInOfficeSum + officer.getTotalDaysInOffice();

            //if sum is more than 10K or equal
            if (totalDaysInOfficeSum >= 10000) {

                //add it in temp list
                tempOffList.add(officer);

                //add in master list
                off.add(tempOffList);

                //reset temp list
                tempOffList = new ArrayList<>();

                //reset sum
                totalDaysInOfficeSum = 0;
                continue;

            }

            //add in temp list
            tempOffList.add(officer);


        }

        //any left over
        if (!tempOffList.isEmpty()) {
            off.add(tempOffList);
        }

        //printint out
        System.out.println("Officers list =" + off.size());

        off.forEach(o -> {

            System.out.println("List size:" + o.size());
            o.forEach(oo -> {
                System.out.println(oo.getName() + "::" + oo.getTotalDaysInOffice());
            });
            System.out.println("====================");
        });
    }
}

输出

Officers list =3
List size:3
John::5000
Matthew::3000
Robert::2000
====================
List size:2
Dave::2000
Patrick::10000
====================
List size:2
YYYY::600
XXXX::600
====================

你希望输出是一个 Map<Officer, Integer> 吗?在这种情况下,你会如何分割分区? - Naman
请查看 https://dev59.com/RlsV5IYBdhLWcg3wuw-M。 - Naman
List<List<Officer>> 可以工作吗? - Eugene
很容易就会想要反过来使用 Map<Integer, List<Officer>>,但那样仍然是错误的... 因为键必须是唯一的,而你无法保证。 - Eugene
@Eugene,我已经使用传统的for循环完成了它。我能否改为使用Java 8的方式来实现? - Makky
显示剩余4条评论
3个回答

2

类似这样:

List<List<Officer>> result = officerList.stream().collect(Collector.of(
            () -> new ArrayList<List<Officer>>(),
            (list, entry) -> {
                if (list.size() == 0) {
                    List<Officer> inner = new ArrayList<>();
                    inner.add(entry);
                    list.add(inner);
                } else {
                    List<Officer> last = list.get(list.size() - 1);
                    int sum = last.stream().mapToInt(Officer::getTotalDaysInOffice).sum();
                    if (sum < 10_000) {
                        last.add(entry);
                    } else {
                        List<Officer> inner = new ArrayList<>();
                        inner.add(entry);
                        list.add(inner);
                    }
                }
            },
            (left, right) -> {
                throw new IllegalArgumentException("Not for parallel");
            }));

谢谢。我会坚持传统的方式,但也会接受这个答案。 - Makky
1
你知道吗,规范中并没有说组合器不能在顺序执行中使用。最简单的组合器实现是迭代包含在“right”中的所有“Officer”实例,并将它们累加到“left”中。这需要将累加器函数存储到本地变量中,在两个位置使用它... - Holger
@Holger 是的,我知道... 我承认昨天我只是太懒了。 - Eugene

0

这里是使用我的库的解决方案:

MutableInt sum = MutableInt.of(0);
List<List<Officer>> off = Stream.of(officerList)
        .splitToList(officer -> sum.getAndSet(sum.value() < 10000 ? sum.value() + officer.getTotalDaysInOffice() : 0) < 10000)
        .toList();

或者:

List<List<Officer>> off = Seq.of(officerList)
        .split(officer -> sum.getAndSet(sum.value() < 10000 ? sum.value() + officer.getTotalDaysInOffice() : 0) < 10000);

0

Java 8 的方式

List<Officer> officerList = new ArrayList<>();

officerList.add(new Officer("John", 5000));
officerList.add(new Officer("Matthew", 3000));
officerList.add(new Officer("Robert", 2000));
officerList.add(new Officer("Dave", 2000));
officerList.add(new Officer("Patrick", 10000));

List<List<Officer>> separatedOfficerLists = officerList.stream()
        .sorted(Comparator.comparing(Officer::getTotalDaysInOffice).reversed())
        .collect(Collectors.groupingByConcurrent(o -> {
            int totalDays = o.getTotalDaysInOffice();
            int divisor = (totalDays / 10000) + 1;
            return (divisor * 10000) - totalDays;
        })).entrySet().stream()
        .sorted(Map.Entry.comparingByKey())
        .map(Map.Entry::getValue)
        .collect(Collectors.toList());

System.out.println(separatedOfficerLists);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接