HashMapを繰り返し処理し(詳細については 以前の質問 を参照)、Mapに含まれるデータで構成される文字列を作成しています。各アイテムには新しい行がありますが、最後のアイテムには新しい行は必要ありません。どうすればこれを達成できますか?エントリが最後のエントリであるかどうかを確認するために何らかのチェックを行うことができると考えていましたが、実際にそれを行う方法はわかりません。
ありがとう!
思考プロセスを「最後を除いてすべて改行する」から「最初を除いてすべて改行する」に変更します。
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());
}
1つの方法(Jon Skeetの彼の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#join を使用します。これらすべての種類のユーティリティ機能を記述することはそれほど難しくありませんが、既存の実績のあるライブラリを再利用する方が常に良いです。 Apache-commons はそのような便利なものでいっぱいです!
For ... eachの代わりにイテレータを使用すると、コードは次のようになります。
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);
1つの解決策は、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.joinソリューションを使用しました。
これは、余分な変数の代わりに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());
}
(謝罪し、例から「借用」してくれたジョンとジョエルに感謝します。)
Foreachループがすべての文字列に新しい行を追加し、ループの終了時に最後の新しい行を削除するためにファイルを通過すると仮定します。
これが最善かどうかはわかりませんが、より簡単な方法です。
すべての値をループし、通常は文字列バッファーに\ nを追加します。次に、このようなことをします
sb.setLength(sb.length()-1);
これは、分割を補完するための結合方法が便利になる場所です。これは、セパレータとして改行を使用してすべての要素を結合でき、もちろん結果の最後に改行を追加しないためです。 ;それが、さまざまなスクリプト言語(Javascript、PHPなど)でそれを行う方法です。
ライブラリにこれを行わせてください。
import com.Sun.deploy.util.StringUtils;
他の多くと同様に、StringUtilsがあり、これには結合メソッドがあります。これは1行で行えます。
StringUtils.join(list, DELIMITER);
しかし、より多くのコンテキストについては、ハッシュマップを使用してそれを行う方法を次に示します。
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);
}
ハ! この投稿 のおかげで、これを行う別の方法が見つかりました:
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はそのためのいくつかのアナログを持っています。
また、最初に配列を作成し、それを繰り返して文字列を作成する必要があるため、これは文字列の追加の反復を行うことを意味することに注意してください。また、will配列を作成します。他のいくつかのメソッドでは、1つの文字列のみをメモリに保持するIEnumerableで取得できる場合があります時間。しかし、私は余分な明快さが本当に好きです。
もちろん、拡張メソッド機能があれば、他のコードも拡張メソッドに抽象化することができます。
Class Separator を使用すると、次のことができます
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());
}
区切り文字は、最初の使用時に空の文字列で印刷され、その後のすべての使用時に区切り文字が印刷されます。
APIを結合するJDK文字列を使用します。
String result = data.stream()
.map((k,v) -> String.format("%s : %s", k, v)
.collect(Collectors.joining("\n"));
trim()
メソッドを使用してこれを実現することもできます。これは、空白を気にせず、StringBuilder
に追加する予定がない場合のみです。
ここにいくつかのサンプルコードがあります
StringBuilder sb = new StringBuilder();
for (Map.Entry<String,String> entry : myMap.entrySet()) {
builder.append(entry.key())
.append(": ")
.append(entry.value())
.append("\n");
}
return sb.toString().trim();