是否可以使用流将字符串组合在一起并计算有序Java列表中的组成员?

时间:2017-09-01 10:50:14

标签: java java-stream

我有一个有序的字符串列表,让我们说PdfReader reader = new PdfReader(FILESIGNED); AcroFields acrofields = reader.getAcroFields(); //pdf have a unique signature String signatureName = acrofields.getSignatureNames().get(0); FileOutputStream os = new FileOutputStream(FILEORIGINAL); InputStream ip = acrofields.extractRevision(signatureName); int n = 0; byte bb[] = new byte[1028]; while ((n = ip.read(bb)) > 0) os.write(bb, 0, n); os.close(); ip.close(); reader.close(); 。我想将相邻的相等字符串组合在一起并计算它们,因此操作的结果应该是"aaa", "aaa", "aaa", "bbb", "bbb", "aaa",如下所示:List。请注意,任务不仅仅是按相同的值进行分组并对它们进行计数(否则我可以简单地将{"aaa":3}, {"bbb":2}, {"aaa", 1}groupingBy一起使用),但只能将具有相同值的相邻字符串分组,如果相同字符串出现在列表的后面,然后它应该被视为单独的。基本上,我需要这段代码来创建一个表头,其中包含现有数据结构中的适当colspans。

我想知道是否有一种使用Java 8流实现此任务的合理方法。我知道如何使用旧式循环来做到这一点,只是认为溪流可以提供更好的方式。

3 个答案:

答案 0 :(得分:1)

就我所见,您可以创建一个自定义收集器,但它与循环没有多大差别(将应用相同的逻辑)。另请注意,我使用SimpleEntry而不是Pair来保存List中的每个元素。

private static Collector<String, ?, List<AbstractMap.SimpleEntry<String, Integer>>> adiacentCollector() {

    class Acc {

        private String previous;

        private List<AbstractMap.SimpleEntry<String, Integer>> result = new ArrayList<>();

        void accumulate(String elem) {
            if (previous == null) {
                previous = elem;
                result.add(new AbstractMap.SimpleEntry<String, Integer>(elem, 1));
                return;
            }

            if (previous.equals(elem)) {
                SimpleEntry<String, Integer> current = result.get(result.size() - 1);
                current.setValue(current.getValue() + 1);
                previous = elem;
            } else {
                SimpleEntry<String, Integer> oneMore = new SimpleEntry<String, Integer>(elem, 1);
                result.add(oneMore);
                previous = elem;
            }
        }

        Acc combine(Acc other) {

            SimpleEntry<String, Integer> lastEntry = result.get(result.size() - 1);
            SimpleEntry<String, Integer> firstEntry = other.result.get(0);

            if (lastEntry.getKey().equals(firstEntry.getKey())) {
                lastEntry.setValue(lastEntry.getValue() + firstEntry.getValue());
                other.result.remove(0);
            }

            result.addAll(other.result);

            return this;

        }

        List<AbstractMap.SimpleEntry<String, Integer>> finisher() {
            return result;
        }

    }
    return Collector.of(Acc::new, Acc::accumulate, Acc::combine, Acc::finisher);
}

并使用它:

 System.out.println(Stream.of("aaa", "aaa", "aaa", "bbb", "bbb", "aaa")
            .collect(adiacentCollector()));

答案 1 :(得分:1)

以下是collapse API提供的解决方案StreamEx

List<Map<String, Long>> res = StreamEx.of("aaa", "aaa", "aaa", "bbb", "bbb", "aaa")
        .collapse(Objects::equals, 
                  Collectors.groupingBy(Function.identity(), Collectors.counting()))
        .toList();

System.out.println(res); // output: [{aaa=3}, {bbb=2}, {aaa=1}]

甚至更简单的'runLength':

List<Map.Entry<String, Long>> res2 = StreamEx.of("aaa", "aaa", "aaa", "bbb", "bbb", "aaa")
                                             .runLengths().toList();

System.out.println(res2); // output: [{aaa=3}, {bbb=2}, {aaa=1}]

答案 2 :(得分:0)

StreamEx是最佳解决方案。 但是在这里另一个使用闭包并减少流:

关闭:

public class StatefulList {
    private List<Pair<String, Integer>> pairList;
    private int index = 0;

    public StatefulList() {
        this.pairList = new ArrayList<>();
    }

    public StatefulList add(String value) {
        if (pairList.size()==0) {
            pairList.add(new Pair<>(value, 1));
            return this;
        }
        Pair<String, Integer> last = pairList.get(index);
        if(last.getKey().equals(value)){
            pairList.set(index, new Pair<>(last.getKey(), last.getValue() + 1));
        } else {
            pairList.add(++index, new Pair<>(value, 1));
        }
        return this;
    }

    public String toString() {
        return pairList.toString();
    }
}

减少它:

Stream.of("aaa", "aaa", "aaa", "bbb", "bbb", "aaa")
      .reduce(new StatefulList(), StatefulList::add, (a,b) -> a);