【问题标题】:Serialize a HashSet<String> with LinQ使用 LinQ 序列化 HashSet<String>
【发布时间】:2010-09-16 19:28:56
【问题描述】:

我想采用HashSet&lt;String&gt; 并将其优雅地转换为字符串。我可以这样迭代:

HashSet<String> words = new HashSet<string>() { "alpha", "beta", "delta" };

string joined = "";
foreach (var w in words) 
  joined += w + ",";

if(joined.Length > 0)
  joined = joined.SubString(0,joined.Length-1); // remove final comma

有没有LinQ 的方式可以优雅而高效地做到这一点?

我能想到的唯一方法是先将其转换为数组:

HashSet<String> words = new HashSet<string>() { "alpha", "beta", "delta" };
string joined = String.Join(",",words.ToArray());

但是,我正在做双重转换。会不会有一些方便、高效、清晰的 LinQ 表达式?

答案 1(来自 marr 的想法)

public static string JoinItems(this IEnumerable<string> items, string joiner) {
    StringBuilder sb = new StringBuilder("");

    foreach (var i in items) 
        sb.AppendFormat("{0}{1}",i,joiner);

    if(sb.Length>0) 
        return sb.Remove(sb.Length - joiner.Length, joiner.Length).ToString();
    else
       return sb.ToString();
}

使用枚举器的答案 2(来自 Martin 的解决方案)

public static string JoinItems<T>(this IEnumerable<T> items, string delim) {
    var sb = new StringBuilder();
    var i = items.GetEnumerator();
    if (i.MoveNext()) {
        sb.Append(i.Current);
        while (i.MoveNext()) {
            sb.Append(delim);
            sb.Append(i.Current);
        }
    }
    return sb.ToString();
}

【问题讨论】:

  • 也许你不知道 string.Join 的重载需要一个可枚举的? (其中包括 HashSet
  • @Kirk - 只有 4.0 有这种过载。 3.5 没有。 msdn.microsoft.com/en-us/library/…

标签: linq string join hashset


【解决方案1】:

我采用了您的方法并将其修改为不需要删除最后一个逗号。我还将AppendFormat 更改为仅Append,因为它避免了每次解析格式的所有工作。

public static string JoinItems(this IEnumerable<string> items, string joiner)
{
    StringBuilder sb = new StringBuilder(); 
    string delim = "";

    foreach (var i in items)
    {
        sb.Append(delim);
        sb.Append(i);
        delim = joiner;
    }

    return sb.ToString(); 
} 

【讨论】:

  • 这会做得很好,我的库版本有签名 Join(this string separator, IEnumerable items) 因为它模仿了 python 语法。各有各的。
  • 非常好!这是一项很棒的技术。
  • 非常好。请注意,对于大型集合,StringBuilder 还是 string.Join 是否更快也存在争议……即使 SB 速度很快,它也可能需要增长/复制缓冲区。 stackoverflow.com/questions/585860/…
  • Slaggg:你说得对,String.Join(",",words.ToArray()) 对于大字符串可能更快,但对于短字符串则不然(OP 已经知道了)。请注意,.Net 4 Join(IEnumerable) 也使用 StringBuilder。
【解决方案2】:

这将在每次迭代时无需额外的副本或检查就可以解决问题:

String JoinItems<T>(IEnumerable<T> items) {
  var stringBuilder = new StringBuilder();
  var i = items.GetEnumerator();
  if (i.MoveNext()) {
    stringBuilder.Append(i.Current);
    while (i.MoveNext()) {
      stringBuilder.Append(", ");
      stringBuilder.Append(i.Current);
    }
  }
  return stringBuilder.ToString();
}

【讨论】:

  • 谢谢。我可能只是修改它,所以 ", " 分隔符不是硬编码的。 (将其添加为参数)。另外,您不需要在i.Current 上致电ToString 吗? stringBuilder.Append(i.Current.ToString());。也许我错了,但你使用的是泛型,所以我认为这是必要的。
  • @Atømix:您不必在i.Current 上致电ToString()。它将使用重载StringBuilder.Append(Object),然后在对象上调用ToString()
【解决方案3】:

我没有在您的 String.Join() 行中看到双重转换。我看到一个转换ToArray(),还不错,然后执行String.Join(),效果不错。

.Net 4 中有一个String.Join(),它采用 IEnumerable,无需转换即可工作。如果您使用的是较旧的框架版本,则可以为字符串编写自己的扩展方法,该方法将分隔符作为“this”参数,然后加入 IEnumerable。请务必使用 stringbuilder 来提高性能。

【讨论】:

  • 我不确定我是否会将其表述为“双重转换”,但它“两次通过”。
  • 问题是,虽然...是否有一个 LinQ 版本是一次通过...或者无论如何是在幕后两次通过?注意:我意识到 ToArray() 当然是一个 LinQ 扩展方法...
  • 这不是 IEnumerable 重载的两次传递,String.Join 使用的字符串数组的唯一方面是对其进行枚举。所以就像我说的,写一个接受枚举的扩展方法,只是不要按照你展示的方式去做,使用一个字符串生成器。
  • 对不起,我很厚。我在看我做的第二个例子,想知道如何实现一个字符串构建器,但是,我现在看到了......你的意思是拿原始的并使用一个字符串构建器。明白了!
猜你喜欢
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 2018-05-07
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多