而不是使用
Collectors.joining(" ")
可以编写一个自定义收集器,在适当的位置添加新行和缩进。
我们来介绍一个 LineWrapper 类,它包含缩进和限制字段:
public class LineWrapper {
private final int limit;
private final String indent;
默认构造函数将字段设置为合理的默认值。
注意缩进是如何以换行符开始的。
public LineWrapper() {
limit = 78;
indent = "\n ";
}
自定义构造函数允许客户端指定限制和缩进:
public LineWrapper(int limit, String indent) {
if (limit <= 0) {
throw new IllegalArgumentException("limit");
}
if (indent == null || !indent.matches("\\n *")) {
throw new IllegalArgumentException("indent");
}
this.limit = limit;
this.indent = indent;
}
Following 是一个正则表达式,用于将输入拆分为一个或多个空格。这样可以确保拆分不会产生空字符串:
private static final String SPACES = " +";
apply 方法将输入拆分并将单词收集成指定最大长度的行,缩进行并删除重复的连续单词。请注意如何使用 Stream.distinct 方法不删除重复项,因为它还会删除不连续的重复项。
public String apply(String input) {
return Arrays.stream(input.split(SPACES)).collect(toWrappedString());
}
toWrappedString 方法返回一个收集器,将单词累积在一个新的 ArrayList 中,并使用以下方法:
- addIfDistinct:将单词添加到 ArrayList
- combine:合并两个数组列表
- 换行:分割和缩进行
.
Collector<String, ArrayList<String>, String> toWrappedString() {
return Collector.of(ArrayList::new,
this::addIfDistinct,
this::combine,
this::wrap);
}
如果单词与前一个单词不同,则 addIfDistinct 将单词添加到累加器 ArrayList 中。
void addIfDistinct(ArrayList<String> accumulator, String word) {
if (!accumulator.isEmpty()) {
String lastWord = accumulator.get(accumulator.size() - 1);
if (!lastWord.equals(word)) {
accumulator.add(word);
}
} else {
accumulator.add(word);
}
}
combine 方法将第二个 ArrayList 中的所有单词添加到第一个。它还确保第二个 ArrayList 的第一个单词不与第一个 ArrayList 的最后一个单词重复。
ArrayList<String> combine(ArrayList<String> words,
ArrayList<String> moreWords) {
List<String> other = moreWords;
if (!words.isEmpty() && !other.isEmpty()) {
String lastWord = words.get(words.size() - 1);
if (lastWord.equals(other.get(0))) {
other = other.subList(1, other.size());
}
}
words.addAll(other);
return words;
}
最后,wrap 方法将所有单词附加到一个 StringBuffer 中,当达到行长限制时插入缩进:
String wrap(ArrayList<String> words) {
StringBuilder result = new StringBuilder();
if (!words.isEmpty()) {
String firstWord = words.get(0);
result.append(firstWord);
int lineLength = firstWord.length();
for (String word : words.subList(1, words.size())) {
//add 1 to the word length,
//to account for the space character
int len = word.length() + 1;
if (lineLength + len <= limit) {
result.append(' ');
result.append(word);
lineLength += len;
} else {
result.append(indent);
result.append(word);
//subtract 1 from the indent length,
//because the new line does not count
lineLength = indent.length() - 1 + word.length();
}
}
}
return result.toString();
}