Как мне добавить символ новой строки для всех строк, кроме последней?
Вопрос
Я перебираю хэш-карту (см. мой предыдущий вопрос для получения более подробной информации) и построения строки, состоящей из данных, содержащихся на Карте.Для каждого элемента у меня будет новая строка, но для самого последнего элемента мне не нужна новая строка.Как я могу этого добиться?Я думал, что мог бы каким-то образом проверить, является ли запись последней или нет, но я не уверен, как на самом деле это сделать.
Спасибо!
Решение
Измените свой мыслительный процесс с "добавляйте разрыв строки все, кроме последнего раза" на "добавляйте разрыв строки все, кроме первого раза".:
boolean first = true;
StringBuilder builder = new StringBuilder();
for (Map.Entry<MyClass.Key,String> entry : data.entrySet()) {
if (first) {
first = false;
} else {
builder.append("\n"); // Or whatever break you want
}
builder.append(entry.key())
.append(": ")
.append(entry.value());
}
Другие советы
один метод (с извинениями перед Джоном Скитом за заимствование части его Java-кода):
StringBuilder result = new StringBuilder();
string newline = "";
for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
result.append(newline)
.append(entry.key())
.append(": ")
.append(entry.value());
newline = "\n";
}
А как насчет этого?
StringBuilder result = new StringBuilder();
for(Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
builder.append(entry.key())
.append(": ")
.append(entry.value())
.append("\n");
}
return builder.substring(0, builder.length()-1);
Обязательные извинения и благодарность Джону и Джоэлу за "заимствование" из их примеров.
Обычно для подобных вещей я использую apache-commons-lang StringUtils#присоединиться.Хотя написать все эти виды утилит на самом деле не сложно, всегда лучше повторно использовать существующие проверенные библиотеки. Apache-общий доступ здесь полно подобных полезных вещей!
Если вы используете iterator вместо for...каждый ваш код может выглядеть следующим образом:
StringBuilder builder = new StringBuilder();
Iterator<Map.Entry<MyClass.Key, String>> it = data.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<MyClass.Key, String> entry = it.next();
builder.append(entry.key())
.append(": ")
.append(entry.value());
if (it.hasNext()) {
builder.append("\n");
}
}
Это, вероятно, лучший пример...
final StringBuilder buf = new StringBuilder();
final String separator = System.getProperty("line.separator"); // Platform new line
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
builder.append(entry.key())
.append(": ")
.append(entry.value())
.append(separator);
}
// Remove the last separator and return a string to use.
// N.b. this is likely as efficient as just using builder.toString()
// which would also copy the buffer, except we have 1 char less
// (i.e. '\n').
final String toUse = builder.substring(0, builder.length()-separator.length()-1);
Вот моя краткая версия, которая использует свойство StringBuilder length вместо дополнительной переменной:
StringBuilder builder = new StringBuilder();
for (Map.Entry<MyClass.Key,String> entry : data.entrySet())
{
builder.append(builder.length() > 0 ? "\n" : "")
.append(entry.key())
.append(": ")
.append(entry.value());
}
(Приношу извинения и благодарю Джона и Джоэла за "заимствование" из их примеров.)
Одним из решений является создание пользовательской оболочки для StringBuilder.Он не может быть расширен, поэтому требуется оболочка.
public class CustomStringBuilder {
final String separator = System.getProperty("line.separator");
private StringBuilder builder = new StringBuilder();
public CustomStringBuilder appendLine(String str){
builder.append(str + separator);
return this;
}
public CustomStringBuilder append(String str){
builder.append(str);
return this;
}
public String toString() {
return this.builder.toString();
}
}
Реализация, подобная такой:
CustomStringBuilder builder = new CustomStringBuilder();
//iterate over as needed, but a wrapper to StringBuilder with new line features.
builder.appendLine("data goes here");
return builder.toString();
У этого действительно есть некоторые недостатки:
- Написание кода, который обычно не ориентирован на "домен / бизнес"
- Не используя стандартное решение с открытым исходным кодом, такое как:StringUtils.присоединяйтесь
- Вынужден поддерживать класс, который обертывает класс JDK, который является окончательным и, следовательно, требует долгосрочных обновлений.
Я выбрал StringUtils.решение join для перебора коллекций и даже создания шаблонов облегченных методов сборки в коде.
Предполагая, что ваш цикл foreach проходит через файл по порядку, просто добавьте новую строку к каждой строке и удалите последнюю новую строку при завершении цикла.
Не уверен, что это лучший способ, но это самый простой способ сделать:
перебирайте все значения и добавляйте обычным образом в stringbuffer .Затем сделайте что-то вроде этого
sb.setLength(sb.length()-1);
Вот где пригодился бы метод join, дополняющий split , потому что тогда вы могли бы просто объединить все элементы, используя новую строку в качестве разделителя, и, конечно, он не добавляет новую строку в конец результата;вот как я делаю это на различных языках сценариев (Javascript, PHP и т.д.).
Если вы используете Разделитель классов, вы можете сделать
StringBuilder buf = new StringBuilder();
Separator separator = new Separator("\n");
for (Map.Entry<MyClass.Key,String> entry: data.entrySet()) {
builder.append(separator)
.append(entry.key())
.append(": ")
.append(entry.value());
}
Разделитель печатается в пустой строке при его первом использовании и разделитель при всех последующих использованиях.
Ha!Благодаря этот пост Я нашел другой способ сделать это:
public static class Extensions
{
public static string JoinWith(this IEnumerable<string> strings, string separator)
{
return String.Join(separator, strings.ToArray());
}
}
Конечно, сейчас это на C #, и Java (пока) не будет поддерживать метод расширения, но вы должны иметь возможность адаптировать его по мере необходимости — главное, это использование String.Join
во всяком случае, и я уверен, что у java есть какой-то аналог для этого.
Также обратите внимание, что это означает выполнение дополнительной итерации строк, потому что вы должны сначала создать массив, а затем выполнить итерацию по нему, чтобы построить свою строку.Кроме того, вы будет создайте массив, где с помощью некоторых других методов вы могли бы обойтись без IEnumerable, который одновременно содержит только одну строку в памяти.Но мне действительно нравится дополнительная ясность.
Конечно, учитывая возможности метода расширения, вы могли бы просто абстрагировать любой другой код в метод расширения.
Позвольте библиотекам сделать это за вас.
import com.sun.deploy.util.StringUtils;
как и многие другие, есть StringUtils, в котором есть метод join .Вы можете сделать это в одной строке:
StringUtils.join(list, DELIMITER);
Но для большего контекста, вот как вы могли бы сделать это с помощью hashmap.
public static String getDelimitatedData(HashMap<String, String> data) {
final String DELIMITER = "\n"; //Should make this a variable
ArrayList<String> entries = new ArrayList<>();
for (Map.Entry<String, String> entry : data.entrySet()) {
entries.add(entry.getKey() + ": " + entry.getValue());
}
return StringUtils.join(entries, DELIMITER);
}
Используйте API-интерфейсы соединения строк JDK:
String result = data.stream()
.map((k,v) -> String.format("%s : %s", k, v)
.collect(Collectors.joining("\n"));