??xml version="1.0" encoding="utf-8" standalone="yes"?>
要回{这个问题不能一概而论Q有时候用Vector比较好;有时是ArrayListQ有时候这两个都不?nbsp;
最好的选择。你别指望能够获得一个简单肯定答案,因ؓq要看你用它们干什么。下面有4个要考虑
的因素:
l API
l 同步处理
l 数据增长?nbsp;
l 使用模式
下面针对q?个方面进行一一探讨
API
在由Ken Arnold{编著的《Java Programming Language?Addison-Wesley, June 2000)一书中有这
L描述QVectorcM于ArrayList.。所有从API的角度来看这两个c非常相[b]伹{但他们之间也还
是有一些主要的区别的?br />同步?/strong>
Vector是同步的。这个类中的一些方法保证了Vector中的对象是线E安全的。而ArrayList则是异步
的,因此ArrayList中的对象q不是线E安全的。因为同步的要求会媄响执行的效率Q所以如果你?nbsp;
需要线E安全的集合那么使用ArrayList是一个很好的选择Q这样可以避免由于同步带来的不必要的
性能开销?nbsp;
数据增长
从内部实现机制来讲ArrayList和Vector都是使用数组(Array)来控刉合中的对象。当你向q两U类
型中增加元素的时候,如果元素的数目超Z内部数组目前的长度它们都需要扩展内部数l的长度Q?nbsp;
Vector~省情况下自动增长原来一倍的数组长度QArrayList是原来的50%,所以最后你获得的这个集
合所占的I间L比你实际需要的要大。所以如果你要在集合中保存大量的数据那么使用Vector有一
些优势,因ؓ你可以通过讄集合的初始化大小来避免不必要的资源开销?nbsp;
使用模式
在ArrayList和Vector中,从一个指定的位置Q通过索引Q查找数据或是在集合的末֢加、移除一
个元素所p的时间是一LQ这个时间我们用O(1)表示。但是,如果在集合的其他位置增加或移?nbsp;
元素那么p的时间会呈线形增长:O(n-i)Q其中n代表集合中元素的个数Qi代表元素增加或移除元
素的索引位置。ؓ什么会q样呢?以ؓ在进行上q操作的时候集合中Wi和第i个元素之后的所有元?nbsp;
都要执行位移的操作。这一切意味着什么呢Q?nbsp;
q意味着Q你只是查找特定位置的元素或只在集合的末端增加、移除元素,那么使用Vector?nbsp;
ArrayList都可以。如果是其他操作Q你最好选择其他的集合操作类。比如,LinkList集合cd增加
或移除集合中M位置的元素所p的时间都是一L—O(1)Q但它在索引一个元素的使用~比较慢
QO(i),其中i是烦引的位置.使用ArrayList也很ҎQ因Z可以单的使用索引来代替创?nbsp;
iterator对象的操作。LinkList也会为每个插入的元素创徏对象Q所有你要明白它也会带来额外的开
销?nbsp;
最后,在《Practical Java》一书中Peter Haggar使用一个简单的数组QArrayQ来代替Vector
或ArrayList。尤其是对于执行效率要求高的E序更应如此。因Z用数l?Array)避免了同步、额?nbsp;
的方法调用和不必要的重新分配I间的操作?/p>
3.实现Runnable接口方式实现多线E?br />如果自己的类已经extends另一个类Q就无法直接extends ThreadQ此Ӟ必须实现一个Runnable接口Q如下:
public class MyThread extends OtherClass implements Runnable {
public void run() {
System.out.println("MyThread.run()");
}
}
Z启动MyThreadQ需要首先实例化一个ThreadQƈ传入自己的MyThread实例Q?br />MyThread myThread = new MyThread();
Thread thread = new Thread(myThread);
thread.start();
事实上,当传入一个Runnable target参数lThread后,Thread的run()Ҏ׃调用target.run()Q参考JDK源代码:
public void run() {
if (target != null) {
target.run();
}
}
4.使用ExecutorService、Callable、Future实现有返回结果的多线E?/strong>
ExecutorService、Callable、Futureq个对象实际上都是属于Executor框架中的功能cR想要详l了解Executor框架的可以访?a >http://www.javaeye.com/topic/366591 Q这里面对该框架做了很详l的解释。返回结果的U程是在JDK1.5中引入的新特征,实很实用,有了q种特征我就不需要再Z得到q回D大费周折了Q而且即便实现了也可能漏洞癑և?br />可返回值的d必须实现Callable接口Q类似的Q无q回值的d必须Runnable接口。执行Callabled后,可以获取一个Future的对象,在该对象上调用get可以获取到Callabledq回的Object了,再结合线E池接口ExecutorService可以实C说中有返回结果的多线E了。下面提供了一个完整的有返回结果的多线E测试例子,在JDK1.5下验证过没问题可以直接用。代码如下:
import java.util.concurrent.*;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
/**
* JavaU程Q有q回值的U程
*
* @author wb_qiuquan.ying
*/
@SuppressWarnings("unchecked")
public class Test {
public static void main(String[] args) throws ExecutionException,
InterruptedException {
System.out.println("----E序开始运?---");
Date date1 = new Date();
int taskSize = 5;
// 创徏一个线E池
ExecutorService pool = Executors.newFixedThreadPool(taskSize);
// 创徏多个有返回值的d
List<Future> list = new ArrayList<Future>();
for (int i = 0; i < taskSize; i++) {
Callable c = new MyCallable(i + " ");
// 执行dq获取Future对象
Future f = pool.submit(c);
// System.out.println(">>>" + f.get().toString());
list.add(f);
}
// 关闭U程?br /> pool.shutdown();
// 获取所有ƈ发Q务的q行l果
for (Future f : list) {
// 从Future对象上获取Q务的q回|q输出到控制?br /> System.out.println(">>>" + f.get().toString());
}
Date date2 = new Date();
System.out.println("----E序l束q行----Q程序运行时间?
+ (date2.getTime() - date1.getTime()) + "毫秒?);
}
}
class MyCallable implements Callable<Object> {
private String taskNum;
MyCallable(String taskNum) {
this.taskNum = taskNum;
}
public Object call() throws Exception {
System.out.println(">>>" + taskNum + "d启动");
Date dateTmp1 = new Date();
Thread.sleep(1000);
Date dateTmp2 = new Date();
long time = dateTmp2.getTime() - dateTmp1.getTime();
System.out.println(">>>" + taskNum + "dl止");
return taskNum + "dq回q行l果,当前d旉? + time + "毫秒?;
}
}
代码说明Q?br />上述代码中Executorsc,提供了一pd工厂Ҏ用于创先U程池,q回的线E池都实CExecutorService接口?br />public static ExecutorService newFixedThreadPool(int nThreads)
创徏固定数目U程的线E池?br />public static ExecutorService newCachedThreadPool()
创徏一个可~存的线E池Q调用execute 重用以前构造的U程Q如果线E可用)。如果现有线E没有可用的Q则创徏一个新U程q添加到池中。终止ƈ从缓存中U除那些已有 60 U钟未被使用的线E?br />public static ExecutorService newSingleThreadExecutor()
创徏一个单U程化的Executor?br />public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
创徏一个支持定时及周期性的d执行的线E池Q多数情况下可用来替代TimercR?/p>
ExecutoreService提供了submit()ҎQ传递一个CallableQ或RunnableQ返回Future。如果Executor后台U程池还没有完成Callable的计,q调用返回Future对象的get()ҎQ会d直到计算完成?/p>
在java中有3个类来负责字W的操作?
1.Character 是进行单个字W操作的Q?/p>
2.String 对一串字W进行操作。不可变cR?/p>
3.StringBuffer 也是对一串字W进行操作,但是可变cR?/p>
String:
是对象不是原始类?
Z可变对象,一旦被创徏,׃能修改它的?
对于已经存在的String对象的修攚w是重新创Z个新的对?然后把新的g存进?
String 是finalc?即不能被l承.
StringBuffer:
是一个可变对?当对他进行修改的时候不会像String那样重新建立对象
它只能通过构造函数来建立,
StringBuffer sb = new StringBuffer();
note:不能通过付值符号对他进行付?
sb = "welcome to here!";//error
对象被徏立以?在内存中׃分配内存I间,q初始保存一个null.向StringBuffer
中付值的时候可以通过它的appendҎ.
sb.append("hello");
字符串连接操作中StringBuffer的效率要比String?
String str = new String("welcome to ");
str += "here";
的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最?br />再将StringBuffer toSting();
q样的话String的连接操作就比StringBuffer多出了一些附加操?当然效率上要打折?
q且׃String 对象是不可变对象,每次操作Sting 都会重新建立新的对象来保存新的?
q样原来的对象就没用?p被垃圑֛?q也是要影响性能?
看看以下代码Q?br />?6个英文字母重复加?000ơ,
可惜我的计算Z是超U计机Q得到的l果每次不一定一样一般ؓ 46687左右?br />也就?6U?br />我们再看看以下代?/p>
得到的结果ؓ 16 有时q是 0
所以结论很明显QStringBuffer 的速度几乎是String 上万倍。当然这个数据不是很准确。因为@环的ơ数?00000ơ的时候,差异更大。不信你试试?/p>
Ҏ上面所_
str += "here";
的处理步骤实际上是通过建立一个StringBuffer,让侯调用append(),最?br />再将StringBuffer toSting();
所以str += "here";可以{同?/p>
StringBuffer sb = new StringBuffer(str);
sb.append("here");
str = sb.toString();
所以上面直接利?+"来连接String的代码可以基本等同于以下代码
q_执行旉?6922左右Q也是46U?/p>
ȝ: 如果在程序中需要对字符串进行频J的修改q接操作的话.使用StringBuffer性能会更?br />
===========================================================================================
Z么会出现那么多比较String和StringBuffer的文章?
原因在于当改变字W串内容Ӟ采用StringBuffer能获得更好的性能。既然是Z获得更好的性能Q那么采用StringBuffer能够获得最好的性能吗?
{案是NOQ?
Z么?
如果你读q《Think in Java》,而且寚w面描qHashTable和HashMap区别的那部分章节比较熟悉的话Q你一定也明白了原因所在。对Q就是支持线E同步保证线E安全而导致性能下降的问题?span style="color: red;">HashTable是线E安全的Q很多方法都是synchronizedҎQ而HashMap不是U程安全的,但其在单U程E序中的性能比HashTable要高。StringBuffer和StringBuildercȝ区别也在于此Q?span style="color: red;">新引入的StringBuildercM是线E安全的Q但其在单线E中的性能比StringBuffer?/span>。如果你Ҏ不太怿Q可以试试下面的例子Q?/p>
package com.hct.test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* @author: chengtai.he
* @created:2009-12-9 上午09:59:57
*/
public class StringBuilderTester {
private static final String base = " base string. ";
private static final int count = 2000000;
public static void stringTest() {
long begin, end;
begin = System.currentTimeMillis();
String test = new String(base);
for (int i = 0; i < count/100; i++) {
test = test + " add ";
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used String. ");
}
public static void stringBufferTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuffer test = new StringBuffer(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuffer. ");
}
public static void stringBuilderTest() {
long begin, end;
begin = System.currentTimeMillis();
StringBuilder test = new StringBuilder(base);
for (int i = 0; i < count; i++) {
test = test.append(" add ");
}
end = System.currentTimeMillis();
System.out.println((end - begin)
+ " millis has elapsed when used StringBuilder. ");
}
public static String appendItemsToStringBuiler(List list) {
StringBuilder b = new StringBuilder();
for (Iterator i = list.iterator(); i.hasNext();) {
b.append(i.next()).append(" ");
}
return b.toString();
}
public static void addToStringBuilder() {
List list = new ArrayList();
list.add(" I ");
list.add(" play ");
list.add(" Bourgeois ");
list.add(" guitars ");
list.add(" and ");
list.add(" Huber ");
list.add(" banjos ");
System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));
}
public static String appendItemsToStirngBuffer(List list) {
StringBuffer b = new StringBuffer();
for (Iterator i = list.iterator(); i.hasNext();) {
b.append(i.next()).append(" ");
}
return b.toString();
}
public static void addToStringBuffer() {
List list = new ArrayList();
list.add(" I ");
list.add(" play ");
list.add(" Bourgeois ");
list.add(" guitars ");
list.add(" and ");
list.add(" Huber ");
list.add(" banjos ");
System.out.println(StringBuilderTester.appendItemsToStirngBuffer(list));
}
public static void main(String[] args) {
stringTest();
stringBufferTest();
stringBuilderTest();
addToStringBuffer();
addToStringBuilder();
}
}
上面的程序结果如下:
5266 millis has elapsed when used String.
375 millis has elapsed when used StringBuffer.
281 millis has elapsed when used StringBuilder.
I play Bourgeois guitars and Huber banjos
I play Bourgeois guitars and Huber banjos
从上面的l果来看Q这三个cd单线E程序中的性能差别一目了Ӟ采用String对象Ӟ即ɘq行ơ数仅是采用其他对象?/100Q其执行旉仍然比其他对象高?5倍以上;而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显Q前者是后者的1.5倍左叟뀂由此可见,如果我们的程序是在单U程下运行,或者是不必考虑到线E同步问题,我们应该优先使用StringBuilderc;当然Q如果要保证U程安全Q自焉StringBuffer莫属了?/p>
除了对多U程的支持不一样外Q这两个cȝ使用几乎没有M差别Q上面的例子是个很好的说明。appendItemsToStringBuiler和appendItemsToStirngBuffer两个Ҏ除了采用的对象分别ؓStringBuilder和StringBuffer外,其他完全相同Q而效果也完全相同?/p>