使用流收集器的java组对象

ajsxfq5m  于 2021-07-07  发布在  Java
关注(0)|答案(1)|浏览(435)

我有对象列表,比如类文档:

class Document {

    private final String id;
    private final int length;

    public Document(String id, int length) {
        this.id = id;
        this.length = length;
    }

    public int getLength() {
        return length;
    }
}

手头的任务是将它们分组在信封中,以便页数(document.length)不超过某个数字。

class Envelope {

    private final List<Document> documents = new ArrayList<>();
}

举个例子,如果我有以下文件列表:

Document doc0 = new Document("doc0", 2);
Document doc1 = new Document("doc1", 5);
Document doc2 = new Document("doc2", 5);
Document doc3 = new Document("doc3", 5);

信封中的最大页数是7页,比我预期的3个包含以下文档的信封还要多:

Assert.assertEquals(3, envelopeList.size());

Assert.assertEquals(2, envelopeList.get(0).getDocuments().size()); // doc0, doc1
Assert.assertEquals(1, envelopeList.get(1).getDocuments().size()); // doc2
Assert.assertEquals(1, envelopeList.get(2).getDocuments().size()); // doc3

我已经用传统的for循环和if实现了这一点,但问题是,有没有可能用流和收集器实现更优雅的方式呢?
谢谢并致以最诚挚的问候
达利博尔

zpqajqem

zpqajqem1#

对于基于长度的批处理文档,我们需要保持累积长度的状态。 Streams 当需要维护外部状态时,不是最佳选择,自定义循环应该是更简单有效的选择。
如果我们强制匹配,这个场景中的流 DocumentSpliterator 变化如下:

public static List<Couvert> splitDocuments(List<Document> docs) {

    IntUnaryOperator helper = new IntUnaryOperator() {
        private int bucketIndex = 0;
        private int accumulated = 0;

        public synchronized int applyAsInt(int length) {
            if (length + accumulated > MAX) {
                bucketIndex++;
                accumulated = 0;
            }
            accumulated += length;
            return bucketIndex;
        }
    };

    return new ArrayList<>(docs.stream()
                               .map(d -> new AbstractMap.SimpleEntry<>(helper.applyAsInt(d.getLength()), d))
                               .collect(Collectors.groupingBy(AbstractMap.SimpleEntry::getKey,
                                       Collector.of(Couvert::new,
                                               (c, e) -> c.getDocuments().add(e.getValue()),
                                               (c1, c2) -> {c1.getDocuments().addAll(c2.getDocuments());return c1;})))
                               .values());
}

说明: helper 保持累计长度,并在超过我使用的最大值时提供一个新的bucket索引 IntUnaryOperator 接口在这里。或者,我们可以使用任何采用 int 参数并返回 int .
关于溪流, Document Map到 SimpleEntry 关于bucketindex和文档。
溪流 SimpleEntry 首先根据bucketindex进行分组。另一个 Collector 改变了 Document 对于一个特定的bucketindex到 Couvert . 输出 collect()Map<Integer,Couvert> 最后是 CollectionCouvert 转换为列表并返回。
注意:对于这个实现,我删除了 front 参数,并将其作为 docs 列表。

相关问题