如何在Java 8中使用reduce对列表进行排序?

4

我有一个对象列表 List<BoM>,在 BoM 中我有一个 List<BoMLine>,现在我需要使用 reduce 方法按照 BoMLine 的某个属性对 BoM 列表进行排序,并在一个方法中返回排序后的列表。

public static List<BoM> sortBoms() {
    List<BoM> sortedBomList = new ArrayList<>();
    BoM bomn = new BoM();
    sortedBomList.add(bomList.parallelStream().reduce(bomn,
            (bom1, bom2) -> sortBoM(bom1, bom2)));
    System.out.println(sortedBomList.size());
    return sortedBomList;
}
BomList 是 BoM 列表,sortBoM 方法用于排序:
private static BoM sortBoM(BoM bom1, BoM bom2) {
    bom2.getLine().stream()
            .sorted((l1, l2) -> l1.getLineNum().compareTo(l2.getLineNum()));
    bom1 = bom2;
    return bom1;
}

BoM类:

public class BoM implements Domain {

private String BomCode;
private List<BoMLine> line = new ArrayList<BoMLine>();

public String getBomCode() {
    return BomCode;
}

public void setBomCode(String bomCode) {
    BomCode = bomCode;
}

public List<BoMLine> getLine() {
    return line;
}

public void setLine(List<BoMLine> line) {
    this.line = line;
}

public void addLine(BoMLine bomLine) {
    bomLine.setbOM(this);
    line.add(bomLine);
}}

以及 BoMLine 类:

public class BoMLine implements Domain {

private Long lineNum;
private String material;
private BigDecimal Qty;
private BoM bOM;

public Long getLineNum() {
    return lineNum;
}

public void setLineNum(Long lineNum) {
    this.lineNum = lineNum;
}

public String getMaterial() {
    return material;
}

public void setMaterial(String material) {
    this.material = material;
}

public BigDecimal getQty() {
    return Qty;
}

public void setQty(BigDecimal qty) {
    Qty = qty;
}

public BoM getbOM() {
    return bOM;
}

public void setbOM(BoM bOM) {
    this.bOM = bOM;
}

public String getBoMCode() {
    return bOM.getBomCode();
}

@Override
public String toString() {
    return "BoMLine [ bOM=" + bOM.getBomCode() + ", lineNum=" + lineNum
            + ", material=" + material + ", Qty=" + Qty + "]";
}}

我需要按照BomLine的lineNum来排序BoM清单,但是只返回了bomList的一个对象。有什么帮助吗?

你能提供一个输入和期望输出的例子吗?我不确定我理解每个“BoM”应该如何排序。 - Tunaki
修改了帖子,请查看。 - Nazila
我仍然不理解BoM对象应该如何排序。您可以按行号对List<BoMLine>进行排序,但这并不能帮助比较两个BoM - Tunaki
我不想将 BoMs 进行比较,只需对每个 BoM 的 BoMLines 进行排序并返回 list<BoM>。 - Nazila
2
@nazila:在你的问题中,你说“现在我必须对BoM列表进行排序”。如果那不是你要排序的列表,请相应地表达你的问题。 - Holger
你是正确的,我需要编辑这个问题。 - Nazila
3个回答

2

您可以使用Comparator.comparing创建自定义Comparator,以按行号升序对每个BoMLine进行排序:

List<BoM> sortedBomList = new ArrayList<>();
sortedBomList.forEach(bom -> bom.getLine().sort(comparing(BoMLine::getLineNum)));

请注意,这将改变List<BoMLine>List<BoM>,这可能不是一个好主意。
更好的方法是采用不可变性,并创建一个构造函数,以获取bom代码和bom行列表:
List<BoM> sortedBomList = 
        bomList.stream()
               .map(bom -> new BoM(
                               bom.BomCode,
                               bom.getLine().stream()
                                            .sorted(comparing(BoMLine::getLineNum))
                                            .collect(toList())
                           )
               )
               .collect(toList());

1
@nazila 你说“必须使用reduce”是什么意思?你不能使用reduce对列表进行排序,因为这不是该方法的用途。你真正想要做什么? - Tunaki
你可以使用reduce实现所有这些方法,但我不知道如何实现。 - Nazila
2
@nazila 你知道reduce方法是干什么的吗?它将一个元素列表缩减(或收集)为单个元素。例如:对列表中所有值求和(这将把数字列表缩减为单个结果:所有元素的总和)。这不会进行排序。 - Tunaki

0

使用reduce无法获得排序后的列表。要获取排序后的列表,您需要在sortBoms()方法中使用sorted,然后使用返回列表的collect逻辑。

修改后的代码如下所示:

sortedBomList.add(bomList.parallelStream().sorted(byBoMLineProperty).collect(Collectors.toList());

这也将涉及实现 byBoMLineProperty 比较器,通过其 BoMLine 属性比较两个 BoM。


-2

你在发布许多无关代码的情况下,过度复杂化了你的问题。你所问的是如何使用reduce对数字列表进行排序,因此整个问题可以有效地简化为实现方法List<Integer> sorted(List<Integer> list)

请注意,尽管这是可能的,但它是低效的,建议采用Tunaki展示的解决方案。我猜这是某个大学作业的一部分。

import org.junit.Test;

import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static junit.framework.Assert.assertEquals;

public class ReductionSortingTest {

    @Test
    public void sortsTheList() {
        List<Integer> list =     asList(5,  3, 9, 8, 15, -4, 9);
        List<Integer> expected = asList(-4, 3, 5, 8, 9,  9,  15);

        List<Integer> sorted = sorted(list);

        assertEquals(expected, sorted);
    }

    private static List<Integer> sorted(List<Integer> list) {
        PriorityQueue<Integer> pq = list.stream()
                .map((Integer n) -> new PriorityQueue<>(singleton(n)))
                .reduce(new PriorityQueue<>(), (pq1, pq2) -> {
                    pq1.addAll(pq2);
                    return pq1;
                });

        List<Integer> result = new ArrayList<>();
        while (!pq.isEmpty()) {
            result.add(pq.poll());
        }
        return result;
    }

}

Java缺少排序列表,因此我决定使用PriorityQueue,在this question中了解更多信息。PriorityQueue保证顶部元素(通过peek()poll()方法访问)是根据元素的自然排序最低的。其迭代器返回的元素顺序不被保证,因此我们必须使用poll()方法清空队列 - 你可能想要实现自己的SortedList类。

编辑:

这里有一个排序列表的解决方案。请记住,尽管没有最终迭代并且它使用二进制搜索,但其效率远非简单排序(我们大概谈论的是O(n^3))。

private List<Integer> sorted(List<Integer> list) {
    return list.stream()
            .map(SortedIntList::new)
            .reduce(new SortedIntList(), (a, b) -> {
                a.addAll(b);
                return a;
            });
}

private static class SortedIntList extends ArrayList<Integer> {

    public SortedIntList() {}

    public SortedIntList(int element) {
        super(singletonList(element));
    }

    @Override
    public boolean add(Integer integer) {
        int insertionPoint = Collections.binarySearch(this, integer);
        if (insertionPoint < 0) {
            insertionPoint = -insertionPoint - 1;
        }
        super.add(insertionPoint, integer);
        return true;
    }

    @Override
    public boolean addAll(Collection<? extends Integer> c) {
        c.forEach(this::add);
        return true;
    }

}

2
你没有使用reduce进行排序,你只是向一个本质上已经排序的集合中添加元素。顺便说一下,这是reduce的无效用法之一,因为它会改变输入值。 - Holger
@Holger 1. 就我理解的问题而言,任务是使用 stream().reduce(...) 而不是 stream().sorted() 进行排序 - 我们都知道这没有多大意义,Tunaki已经详细解释了。2. 我使用 reduce 的方式是完全有效的。我决定改变输入值而不是每次创建新集合,因为效率更高,但是所有这些优先队列仅在此方法内部使用。 - Jaroslaw Pawlak
3
也许您需要先阅读文档,了解“Reduction”和“Mutable Reduction”的区别。对于后者,有一个专门的方法collect可供使用,而不是滥用reduce。您不是决定使用是否“绝对有效”的人,API设计者决定什么是正确的用法,他们已经做出了决定。 - Holger
@Holger,我认为你已经表达了你的观点。如果你想展示“正确”的方式,请发表一个回答。虽然你的评论并没有超出界限,但对于未来的访客来说并没有什么用处,因为这需要他们绕过你和OP之间的争论。 - George Stocker

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