我有一个有序的字符串列表,让我们说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流实现此任务的合理方法。我知道如何使用旧式循环来做到这一点,只是认为溪流可以提供更好的方式。
答案 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);