题
我正在迭代 HashMap(请参阅 我之前的问题 了解更多详细信息)并构建一个由地图中包含的数据组成的字符串。对于每个项目,我都会有一个新行,但对于最后一个项目,我不需要新行。我怎样才能实现这个目标?我想我可以进行某种检查以查看该条目是否是最后一个条目,但我不确定如何实际执行此操作。
谢谢!
解决方案
这是“追加行打破了所有,但最后一次”到“前面加上一个换行符所有,但第一次”改变你的思维过程:
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);
强制性道歉和由于两者Jon和乔用于从它们的实例“借”。
通常对于这些事情我使用 apache-commons-lang StringUtils#join. 。虽然编写所有这些类型的实用程序功能并不难,但重用现有的经过验证的库总是更好。 Apache 共享资源 充满了这样有用的东西!
如果您使用迭代器,而不是为每个...你的代码看起来是这样的:
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的长度属性,而不是一个额外的变量的:
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());
}
(道歉和由于两者Jon和乔用于从它们的实例“借”。)
一种解决方案是创建 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
- 被迫维护一个包装 JDK 类的类,该类是最终的,因此需要长期更新。
我使用 StringUtils.join 解决方案来迭代集合,甚至在代码中构建轻量级构建方法模式。
假设你的foreach循环通过文件,以便只是一个新的行添加到每个字符串,并删除最后一个新行去当你的循环退出。
不知道这是否是最好的,但it's更简单的方式来做到:
通过所有值循环,并在追加的StringBuffer成\ n正常。然后,做这样的事情。
sb.setLength(sb.length()-1);
这是一个地方连接方法,以补充分裂,会派上用场,因为这样你可以使用一个新行作为分隔符刚刚加入的所有元素,当然它一个新行不追加到尾部结果;这就是我如何做到这一点的各种脚本语言(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());
}
在其第一次使用在空字符串的分离器打印,并且在所有后续使用的分离器。
哈!由于这个帖子我找到了另一种方式来做到这一点:
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的,它有一个连接方法。您可以在一行做到这一点:
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);
}
使用JDK串接合的API:
String result = data.stream()
.map((k,v) -> String.format("%s : %s", k, v)
.collect(Collectors.joining("\n"));