如何通过 stream() 将所有对象列表(包含一些文本)连接到一个大列表中

How to concatenate all lists of objects (containing some text) within one big list through the means of stream()

我得到: TextInfo 对象列表的列表。每个 TextInfo 对象都包含一段文本和 return 文本的 toString 覆盖方法。通过一个TextInfo的Y值我们可以得出什么TextInfo在同一行(自定义问题)

我想要: 字符串列表。每个字符串都是一个子列表的所有元素串联的结果。我想尽可能多地使用流。

到目前为止:

List<String> allLinesByYCoordinate = groupAllTextLineByLineBasedOnY(allTextInfosOnPage);

public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
    Map<Object, List<TextInfo>> groupedtextInfosPerLine =  allTextInfo.stream().
                collect(Collectors.groupingBy(x -> x.getY()));
    List<String> allLines = new ArrayList<>();
    for (Map.Entry<Object, List<TextInfo>> groupedtextInfos: groupedtextInfosPerLine.entrySet()) {
        String temp = "";
        for (TextInfo textInfo: groupedtextInfos.getValue()) {
            temp += textInfo;
        }
        allLines.add(temp);
    }
    return allLines;
}

您可能同意 groupAllTextLineByLineBasedOnY 方法看起来有点过时。我正在尝试对大列表中的每个 TextInfo 列表执行连接,从而生成一个字符串列表,其中每个字符串曾经是 TextInfo 的列表

我希望找到一个简洁的 stream() 解决方案

让我们一次重构一点。

首先,你永远不应该在循环中构建一个字符串,因为它可能非常低效;请改用 StringBuilder。但是,我们可以改为流式传输并收集到一个字符串中。另请注意,我们在循环中使用 Map.values() 而不是调用 getValue()。

public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
    Map<Object, List<TextInfo>> groupedtextInfosPerLine = allTextInfo.stream()
                    .collect(Collectors.groupingBy(x -> x.getY()));
    List<String> allLines = new ArrayList<>();
    for (List<TextInfo> groupedtextInfos: groupedtextInfosPerLine.values()) {
        allLines.add(groupedtextInfos.stream().map(Object::toString).collect(Collectors.joining()));
    }
    return allLines;
}

在接下来的重构中,我们可以摆脱中间列表 allLines 而是流式传输 textInfos 并将它们收集到一个列表中:

public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
    Map<Object, List<TextInfo>> groupedtextInfosPerLine =  allTextInfo.stream()
            .collect(Collectors.groupingBy(x -> x.getY()));

    return groupedtextInfosPerLine.values().stream()
            .map(textInfos -> textInfos.stream().map(Object::toString).collect(Collectors.joining()))
            .toList();
}

最后,我们可以去掉 groupedtextInfosPerLine 变量:

public static List<String> groupAllTextLineByLineBasedOnY(List<TextInfo> allTextInfo) {
    return allTextInfo.stream()
            .collect(Collectors.groupingBy(TextInfo::getY)).values().stream()
            .map(textInfos -> textInfos.stream().map(Object::toString).collect(Collectors.joining()))
            .toList();
}

我认为您正在寻找这样的解决方案

import java.util.Collection;
import java.util.List;
import java.util.TreeMap;
import java.util.stream.Collectors;

class Scratch {
    public static void main(String[] args) {
        // Setup some example data
        List<TextInfo> textInfos = List.of(
                new TextInfo("line 3 a", 3),
                new TextInfo("line 1 a", 1),
                new TextInfo("line 2 a", 2),
                new TextInfo("line 1 b", 1),
                new TextInfo("line 3 b", 3),
                new TextInfo("line 1 c", 1)
        );
        
        // This is the actual answer
        Collection<String> allLines = textInfos.stream()
                .collect(Collectors.toMap(
                        TextInfo::getY, // line number as key
                        TextInfo::toString, // convert TextInfo to String
                        (a, b) -> a + b, // Merge TextInfo on the same line
                        TreeMap::new)) // Ensure in order
                .values();

        // You would return allLines from the method
        System.out.println(allLines);
    }

    static class TextInfo {
        String text;
        int y;

        public TextInfo(String text, int y) {
            this.text = text;
            this.y = y;
        }

        public int getY() { return y; }

        @Override
        public String toString() { return text; }
    }
}

如果您 运行 您打印的代码

[line 1 aline 1 bline 1 c, line 2 a, line 3 aline 3 b]