优化此ArrayList连接方法

我已经编写了这段代码来连接ArrayList元素:它可以更优化吗? 或者有更好的不同方式吗?

public static String join(ArrayList list, char delim) { StringBuffer buf = new StringBuffer(); for (int i = 0; i < list.size(); i++) { if (i != 0) buf.append(delim); buf.append((String) list.get(i)); } return buf.toString(); } 

以下是着名的java.util.Collection团队如何做到这一点,所以我认为这应该是相当不错的;)

  421 /* Returns a string representation of this collection. The string 422 * representation consists of a list of the collection's elements in the 423 * order they are returned by its iterator, enclosed in square brackets 424 * ("[]"). Adjacent elements are separated by the characters 425 * ", " (comma and space). Elements are converted to strings as 426 * by {@link String#valueOf(Object)}. 427 * 428 * @return a string representation of this collection 429 */ 430 public String toString() { 431 Iterator i = iterator(); 432 if (! i.hasNext()) 433 return "[]"; 434 435 StringBuilder sb = new StringBuilder(); 436 sb.append('['); 437 for (;;) { 438 E e = i.next(); 439 sb.append(e == this ? "(this Collection)" : e); 440 if (! i.hasNext()) 441 return sb.append(']').toString(); 442 sb.append(", "); 443 } 

另外,这就是你如何用duffymo的答案得到逗号分隔符;)

StringBuffer是为了线程安全而同步的,而是使用StringBuilder。

不要在循环的每次迭代中调用list.size()。 将其设置为变量或使用迭代器。

另请注意,有很多库用于此,主要是谷歌collections 。 请尝试以下方法:

 public String join(List list, char delimiter) { StringBuilder result = new StringBuilder(); for (Iterator i = list.iterator(); i.hasNext();) { result.append(i.next()); if (i.hasNext()) { result.append(delimiter); } } return result.toString(); } 

为什么你需要一个方法? 为什么不直接使用List的toString()方法?

 public static String join(List list) { return list.toString(); // comma delimiter with some extra stuff at start and end } 

你可以比你写的方法做得更好。 添加List接口和generics; 也许甚至collections。 它将更通用,更安全; 不需要演员表。

运行这个类:

 import java.util.Arrays; import java.util.List; public class ListToStringDemo { public static void main(String[] args) { List values = Arrays.asList(args); System.out.println(values); } } 

使用命令行上的任何字符串参数(例如,“foo bar baz bat”)并获得此输出:

 C:\JDKs\jdk1.6.0_13\bin\java ListToStringDemo foo bar baz bat [foo, bar, baz, bat] Process finished with exit code 0 

这是否已知肯定会导致一些性能问题,或者您是否正在将此作为练习? 如果没有,我不会打扰。

这可能会更快,但我对此表示怀疑:

 StringBuffer buf = new StringBuffer(); for (int i = 0; i < list.size() - 1; i++) { buf.append((String) list.get(i)); buf.append(delim); } buf.append((String) list.get(i)); return buf.toString(); 

我的第一个想法是, Google Collection的Joiner课程将是一个有用的起点。 具体来说, public final String join(Iterable parts)方法。

#1

每次循环迭代时,您都在检查列表的大小:

 for (int i = 0; i < list.size(); i++) { 

由于您没有更改列表,因此您只需执行以下操作:

 for (int i = 0, j = list.size(); i < j; i++) { 

#2

而不是在每次迭代中检查i != 0 ,只需在每次迭代后附加一个分隔符:

  for (int i = 0; i < list.size(); i++) { buf.append((String) list.get(i)); buf.append(delim); } // Here, convert all but the last character in the buffer to a string. 

更好,也许:

列表而不是ArrayList

接口程序而非实现

for(String string:strings)而不是for(int i …)

根据Josh的说法,来自1.5的foreach循环实际上更有效率,并且更容易理解

其他人已经注意到等等

我怀疑它可以被显着优化 (意味着变得更快)而不会变得神秘。 但是,可以通过使用generics来改进它。

 public static String join(List list, char delim) { StringBuffer buf = new StringBuffer(512); first = true; for (String item : list) { if (first) { first = false; } else { buf.append(delim); } buf.append(item); } } 

这个版本的改进:你强制要求项目只包含编译器级别的字符串,你不必维护一个循环变量,而且你不需要一个ArrayList,这是你的API不应该的实现细节执行。

除了收到的答案之外,您还可以预先初始化StringBuilderStringBuffer的容量。 这减少了内部数组在扩展时必须重新复制的次数。 由于您不知道所需的确切容量,因此您可以根据元素的数量估算每个元素的平均大小和倍数。

 private static final int ESTIMATE_ELEM_SIZE = 8; public static String join(Collection col, char delim) { int len = col.size(); if (len == 0) return ""; StringBuilder buf = new StringBuilder(ESTIMATE_ELEM_SIZE * len); for (Object elem : col) { buf.append(elem).append(delim); } return buf.substring(0, buf.length() - 1); } 

这显示了其他人共享的一些技术:使用StringBuilderif在循环中检查则不要放置,并使用foreach增强循环。

我还建议将第一个参数更改为有意义的最通用类型,以便它更可重用。 在这种情况下,由于您确实不需要索引来处理每个元素,因此可以指定Collection

注意,也没有必要StringBuilder.append()转换为String因为StringBuilder.append()会在必要时将元素转换为String 。 这允许您将此用于包含除字符串之外的其他类型对象的集合。