#java #string #java-stream
Вопрос:
Мне нужно соединить список строк запятыми. Но также у меня есть ограничение на длину строки. Например, список I need to join a list of strings with commas
должен стать : I, need, to, join, a, n list, of, strings, with, n commas
.
Я уже реализовал метод для этого:
private static String joinLabels(Listlt;Stringgt; strings) { int rowLengthCounter = 0; StringBuilder result = new StringBuilder(); for (String str : strings) { rowLengthCounter = str.length(); if (rowLengthCounter gt; 40) { result.append("n"); rowLengthCounter = str.length(); } result.append(str); result.append(", "); } return result.substring(0, result.lastIndexOf(", ")); }
И это работает. Но, может быть, существует простой метод, такой как String.соединение для соединения строк с указанным разделителем или потоковым API. Спасибо за помощь.
Комментарии:
1. Не существует простого метода в виде существующего класса Java SE, который бы это сделал. Делать это с
Stream
помощью было бы сложно … что противоречит цели использованияStream
.2. Ваша реализация не учитывает символы разделителя, кроме того, ограничение 40 слишком велико для ожидаемого вывода примера. Это непоследовательно. Ожидание примера может быть выполнено с помощью
String.join(", ", strings).replaceAll("\G(?=.{25}).{1,23}, ", "$0n")
Ответ №1:
Можно было бы поспорить о необходимости пара в этом случае. Но другим решением, которое здесь не было приведено, будет создание пользовательского сборщика. Коллектор — это изменяемая операция сокращения и будет выглядеть почти как ваш текущий код цикла for.
public class CustomStringJoiner implements Collectorlt;String, StringBuffer, Stringgt; { private int rowLength = 0; private final String delimiter; private final int maxRowSize; private boolean first; public CustomStringJoiner(String delimiter, int maxRowSize) { this.delimiter = delimiter; this.maxRowSize = maxRowSize; first = true; } @Override public Supplierlt;StringBuffergt; supplier() { return StringBuffer::new; } @Override public BiConsumerlt;StringBuffer, Stringgt; accumulator() { return this::add; } @Override public BinaryOperatorlt;StringBuffergt; combiner() { return this::combine; } @Override public Functionlt;StringBuffer, Stringgt; finisher() { return StringBuffer::toString; } @Override public Setlt;Characteristicsgt; characteristics() { return Set.of(); } private void add(StringBuffer b, String v) { rowLength = v.length(); if (!first) { b.append(delimiter); } else { first = false; } if (rowLength gt; maxRowSize) { b.append("n"); rowLength = v.length(); } b.append(v); } private StringBuffer combine(StringBuffer a, StringBuffer b) { if (a.lastIndexOf("n") == a.length()) { a.append(b); } else { for (final var s : b.toString().split(delimiter)) { add(a, s); } } return a; } }
Вы можете видеть, что add
метод совпадает с вашим циклом for.
используйте его вот так:
final String result = Stream.of(values).collect(new CustomStringJoiner(", ", 10)); System.out.println(result);
Это выведет:
I, need, to, join, a, list, of, strings, with, commas
Ответ №2:
вы можете использовать API потоков и коллекторов java 8 для этого,
private static String joinLables(Listlt;Stringgt; lables) { final AtomicInteger counter = new AtomicInteger(); final Integer chunkSize = 3; final String DELIMITER = ","; return lables.stream() .collect(Collectors.groupingBy(it -gt; counter.getAndIncrement() / chunkSize)).values() .stream() .map(s -gt; String.join(DELIMITER, s)) .reduce((a, b) -gt; a "n" b) .get(); }
Комментарии:
1. вы должны изменить couter.addAndGet(it.length ()), чтобы это работало должным образом
Ответ №3:
Код довольно сложен с потоками, но вы можете написать его так :
public static String joinLabels2(final Listlt;Stringgt; strings,) { final Optionallt;Stringgt; res = strings.stream() .map(s -gt; s ", ") .reduce((s1, s2) -gt; s1.contains("n") amp;amp; (s1 s2).length() - StringUtils.lastIndexOf(s1, "n") gt; 40 || !s1.contains("n") amp;amp; (s1 s2).length() gt; 40 ? s1 s2 "n" : s1 s2) .map(s -gt; s.substring(0, s.length() - 2)); return res.get(); }
Проверьте, изменив максимальную длину с «40» на «10». :
@Test public void test() { System.out.println(joinLabels2(Arrays.asList("I", "need", "to", "join", "a", "list", "of", "strings", "with", "commas"))); }
выходы :
I, need, to, join, a, list, of, strings, with, commas,
Было бы здорово написать условие сокращения следующим образом : (s1 s2).length() gt; 40 ? ...
но после первого разрыва строки условие всегда будет истинным, потому что s1 содержит предыдущее сокращение.
NB: вам нужно точно настроить этот метод, если вы хотите сравнить длину без запятых, потому что я добавил их перед сокращением, но вы этого не сделали в своем исходном коде.
Ответ №4:
Дано:
private static final int maxLength = 15; private static final Listlt;Stringgt; strings = Arrays.asList("I", "need", "to", "join", "a", "list", "of", "strings", "with", "commas");
Я вижу 2 способа. Они оба полагаются на тот факт, что вы можете рассчитать длину текущей строки в StringBuilder таким образом:
int currentLineLength = sb.length() - sb.lastIndexOf("n");
1: Путь потока
- Добавьте запятые в каждую строку, кроме последней
- попробуйте добавить каждый токен в
StringBuilder
- Если добавление следующего токена в текущую строку в StringBuilder превышает максимальное значение, добавьте разрыв строки
- Добавить токен
StringBuilder sb = new StringBuilder(); IntStream.range(0, strings.size()) .mapToObj(i -gt; strings.get(i) (ilt;strings.size()-1 ? ", " : "")) .forEach(s -gt; addToStringBuilder(s, sb)); System.out.println(sb); private void addToStringBuilder(String s, StringBuilder sb) { int currentLineLength = sb.length() - sb.lastIndexOf("n"); if (currentLineLength s.length() gt; maxLength) { sb.append("n"); } sb.append(s); }
2: Или вы можете пойти рекурсивным путем
String result = concatenate(strings, new StringBuilder()); System.out.println(result); private String concatenate(Listlt;Stringgt; remainingTokens, StringBuilder sb) { if(remainingTokens.size() == 0) { return sb.toString(); } String nextToken = remainingTokens.size() gt; 1 ? remainingTokens.get(0) ", " : remainingTokens.get(0); int currentLineLength = sb.length() - sb.lastIndexOf("n"); if (currentLineLength nextToken.length() gt; maxLength) { sb.append("n"); } sb.append(nextToken); return concatenate(remainingTokens.subList(1, remainingTokens.size()), sb); }
Оба вывода:
I, need, to, join, a, list, of, strings, with, commas