1、List遍歷時刪除的幾種方式比較
1.1、會報錯的刪除方式:
(1)在Iterator遍歷時使用list刪除
Iterator<String> it = list.iterator();
while(it.hasNext()){
String item = it.next();
list.remove(item); //報錯!!!
}
(2)foreach遍歷方式中刪除
for(String s : list){
list.remove(s); //報錯!!!
}
以上都是報java.util.ConcurrentModificationException,某個線程在 Collection 上進行迭代時,通常不允許另一個線性修改該 Collection,因爲在這些情況下,迭代的結果是不確定的。
而對於foreach實際上使用的是iterator進行處理的,而iterator是不允許集合在iterator使用期間通過list刪除的,也就是第一種方式,也就是說上面兩種方式相當於是同一種。
1.2、不會報錯,但是有可能漏刪或不能完全的刪除方式:
(1)漏刪的情況(通過索引下標的方式)
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
System.out.println("----------list大小1:--"+list.size());
for (int i = 0; i < list.size(); i++) {
if (2 == list.get(i)) {
list.remove(i);
}
System.out.println(list.get(i));
}
System.out.println("最後輸出=" + list.toString());
輸出的結果如下:
———-list大小1:–5
1
2
3
4
最後輸出=[1, 2, 3, 4]
可以看到,只刪除了一個2,還有一個沒有完全刪除,原因是:刪除了第一個2後,集合裏的元素個數減1,後面的元素往前移了1位,此時,第二個2已經移到了索引index=1的位置,而此時i馬上i++了,list.get(i)獲得的是數據3。
(2)不能完全刪除的情況
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
System.out.println("----------list大小1:--"+list.size());
for (int i = 0; i < list.size(); i++) {
list.remove(i);
}
System.out.println("最後輸出=" + list.toString());
輸出的結果如下:
———-list大小1:–5
最後輸出=[2, 3]
可以看到,結果並沒有按照我們的想法,把所有數據都刪除乾淨。原因是:在list.remove之後,list的大小發生了變化,也就是list.size()一直在變小,而 i 卻一直在加大,當 i =3時,list.size()=2,此時循環的判斷條件不滿足,退出了程序。
以上兩種情況通過for循環遍歷刪除,都沒有正確達到目的,都是因爲在remove後list.size()發生了變化(一直在減少),同時後面的元素會往前移動,導致list中的索引index指向的數據有變化。同時我們的for中的i是一直在加大的!
1.3 List遍歷過程中刪除元素的推薦做法
還是使用Iterator遍歷,但是不用list來remove。如下代碼:
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(2);
list.add(3);
list.add(4);
System.out.println("----------list大小1:--"+list.size());
Iterator<Integer> it = list.iterator();
while(it.hasNext()){
Integer item = it.next();
if (2 == item) {
it.remove();
}
System.out.println(item);
}
System.out.println("最後輸出=" + list.toString());
輸出結果:
———-list大小1:–5
1
2
2
3
4
最後輸出=[1, 3, 4]
此時,兩個2被全部刪除了。
對於iterator的remove()方法,也有需要我們注意的地方:
1、每調用一次iterator.next()方法,只能調用一次remove()方法。
2、調用remove()方法前,必須調用過一次next()方法。
2、Java基本數據類型及包裝類
byte(字節) 8 位 Byte
shot(短整型) 16位 Short
int(整型) 32 位 Integer
long(長整型) 64 位 Long
float(浮點型) 32 位 Float
double(雙精度) 64 位 Double
char(字符型) 16 位 Character
boolean(布爾型) 1 位 Boolean
各數據類型按容量大小(表數範圍大小)由小到大排列爲:
byte <—— short, char <——int <——long <——float <——double
基本類型之間的轉換原則:
1)運算時,容量小的類型自動轉換爲容量大的類型;
2)容量大的類型轉換爲容量小的類型時,要加強制轉換符,且精度可能丟失;
如:float f = 1.2f;
int ff = (int) f;
System.out.println(ff);//輸出爲1,丟掉了小數部分
3)short,char之間不會互相轉換(需要強制轉換),byte、short、char並且三者在計算時首先轉換爲int類型;
4)實數常量默認爲double類型, 整數常量默認爲int類型;
3、switch中的參數類型
在jdk1.7 之前switch 只能支持 byte、short、char、int或者其對應的封裝類以及 Enum 類型。
如:
enum EnumTest {
LEFT,
RIGHT
}
EnumTest e = EnumTest.LEFT;
switch (e) {
case LEFT:
System.out.println("----left-----");
break;
default:
break;
}
在jdk1.7 及1.7以後,switch也支持了String類型,如下:
String str = "abc";
switch (str) {
case "abc":
System.out.println("-----abc-----");
break;
case "aaa":
System.out.println("-----aaa-----");
break;
}
4、equals與==的區別
(1)==是一個運算符,它比較的是值
對於基本數據類型,直接比較其數據值是否相等。如果是不同的基本數據類型之間進行比較,則遵循基本數據類型間運算的轉換原則(見上面總結的第二條)。如下:
if(12 == 12.0){
System.out.println("-----12 == 12.0-------");
}
此時打印了—–12 == 12.0——-,因爲低一級的int類型的12自動轉換爲高一級的float類型
對於引用類型,==比較的還是值,只不過此時比較的是兩個對象變量的內存地址。所以,用==來比較對象,實際上是判斷這兩個對象是否是同一個new出來的對象,或者是否是一個對象賦值給另一個對象的情況。如:
String s1 = new String(“abc”);
String s2 = s1;//將s1對的內存地址賦給了s2,此時s1==s2返回true;
(2)equals
equals方法是屬於Object類的一個方法,其實現源碼如下:
public boolean equals(Object obj) {
return (this == obj);
}
可以看到,其實equals方法裏面用的還是==運算符,所以對於那些沒有重寫過Object類的equals方法來說,==和equals方法是等價的!
然而,很多類都自己去重寫了equals方法,比如String類、所有基本數據類型的包裝類等
String類的equals源碼如下:
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
首先判斷是否是同一個new出來的對象,即判斷內存地址是否相同;如果不同則判斷對象中的內容是否相同。
Integer類的equals方法如下:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
直接轉成判斷值是否相等了。
因此,對於String類和所有基本數據類型的包裝類來說,equals方法就是判斷其內容是否相等。對於其他類來說,要具體看其是否重寫了equals方法及具體業務實現。
另:對於基本數據類型來說,使用equals方法,需要用該基本類型對應的包裝類,因爲equals是針對對象來使用的!
5、Object有哪些公用方法
Object類中的所有方法如下:
public boolean equals(Object obj) {//判斷是否同一個對象,具體見上一點總結
return (this == obj);
}
public String toString(){
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
//返回該對象的哈希碼值,重寫了equals方法一般都要重寫hashCode方法
public native int hashCode();
/**
*wait方法就是使當前線程等待該對象的鎖,當前線程必須是該對象的擁有者,也就是具有該對象的鎖。wait()方法一直等待,直到獲得鎖或者被中斷。wait(long timeout)設定一個超時間隔,如果在規定時間內沒有獲得鎖就返回。
*調用該方法後當前線程進入睡眠狀態,直到以下事件發生。
*(1)其他線程調用了該對象的notify方法。
*(2)其他線程調用了該對象的notifyAll方法。
*(3)其他線程調用了interrupt中斷該線程。
*(4)時間間隔到了。
*此時該線程就可以被調度了,如果是被中斷的話就拋出一個InterruptedException異常。
*如:Person p = new Person();
*p.wait()//使用Person p對象作爲對象鎖。
*/
public final void wait() throws InterruptedException {...}
public final native void wait(long timeout) throws InterruptedException;
public final void wait(long timeout, int nanos) throws InterruptedException {...}
//該方法喚醒在該對象上等待的某個線程。如p.notify();
public final native void notify();
//該方法喚醒在該對象上等待的所有線程。
public final native void notifyAll();
public final native Class<?> getClass();//獲得運行時類型
//創建並返回此對象的一個副本。只有實現了Cloneable接口纔可以調用該方法,否則拋出CloneNotSupportedException異常。
protected native Object clone() throws CloneNotSupportedException;
//用於釋放資源。當垃圾回收器確定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。也可手動調用,自己實現一些資源的釋放。
protected void finalize() throws Throwable { }
6、Java中的四種引用:強引用、軟引用、弱引用、虛引用
四種級別由高到低依次爲:強引用 > 軟引用 > 弱引用 > 虛引用
【 參考文章:
http://blog.csdn.net/ocean181/article/details/7232759
http://my.oschina.net/ydsakyclguozi/blog/404389
http://blog.csdn.net/hubenshan/article/details/7735858/#comments
】
6.1 強引用(StrongReference)
強引用是使用最普遍的引用。如果一個對象具有強引用,那垃圾回收器絕不會回收它。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具有強引用的對象來解決內存不足的問題。如下的定義方式:
String str = new String("abc"); //強引用,在堆中創建了String這個對象,通過棧中的變量str引用這個對象
String str2 = str; //強引用,str2也指向了堆中創建的String對象
這兩個引用都是強引用.只要存在對堆中String對象的引用,gc就不會回收該對象,如果通過下面代碼:str = null; str2 = null;顯示的設置引用str和str2爲null,則gc就會認爲堆中的String對象已經不存在其他引用了,此時該對象處於可回收的狀態,但是到底什麼時候回收該對象,取決於gc的算法。
6.2 軟引用(SoftReference)
如果一個對象只具有軟引用,則內存空間足夠,垃圾回收器就不會回收它;如果內存空間不足了,就會回收這些對象的內存。只要垃圾回收器沒有回收它,該對象就可以被程序使用。軟引用可用來實現內存敏感的高速緩存。如下使用代碼:
String str= new String("abc"); //強引用
Refenrence sr = new SoftReference(str); //軟引用
//引用時
if(sr!=null){
str= sr.get();
}else{
str= new String("abc");
sr = new SoftReference(str);
}
可以看到不論是強引用、軟引用、弱引用或者虛引用都是針對某個對象來說的,當我們某個對象需要設置爲軟引用時,只需要給該對象套入到軟引用對象中即可,如上面的代碼
SoftReference sr = new SoftReference(str);
由於軟引用在內存不足時可以被回收,在內存充足時不會被回收,所以軟引用經常被用來作爲緩存使用。比如在Android中經常把Bitmap作爲軟引用來緩存圖片,如
HashMap<String, SoftReference<Drawable>> imageCache;
軟引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果軟引用所引用的對象被垃圾回收器回收,Java虛擬機就會把這個軟引用加入到與之關聯的引用隊列中。
6.3 弱引用(WeakReference)
弱引用與軟引用的區別在於:只具有弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它所管轄的內存區域的過程中,一旦發現了只具有弱引用的對象,不管當前內存空間足夠與否,都會回收它的內存。不過,由於垃圾回收器是一個優先級很低的線程,因此不一定會很快發現那些只具有弱引用的對象。
弱引用可以和一個引用隊列(ReferenceQueue)聯合使用,如果弱引用所引用的對象被垃圾回收,Java虛擬機就會把這個弱引用加入到與之關聯的引用隊列中。
對於軟引用或者弱引用來說,gc回收軟引用或弱引用對象的過程是一樣的,其執行過程如下:
String str= new String("abc"); //強引用
Refenrence sr = new SoftReference(str); //軟引用
- 首先將軟引用或弱引用的referent設置爲null(即置str = null;),不再引用堆中的對象;
- 將堆中的對象new String(“abc”);設置爲可結束的(finalizable)。
當heap中的new String(“abc”)對象的finalize()方法被運行而且該對象佔用的內存被釋放, sr被添加到它的ReferenceQueue中。
可以用如下代碼來說明過程:
String str = new String("abc");
SoftReference<String> soft = new SoftReference<String>(str); //軟引用
str = null;
System.out.println("before gc:" + soft.get());
System.gc();
System.out.println("after gc:" + soft.get());
輸出結果:before gc: abc
after gc: abc
對於弱引用:
String str = new String("abc");
WeakReference<String> soft = new WeakReference<String>(str); //弱引用
str = null;
System.out.println("before gc:" + soft.get());
System.gc();
System.out.println("after gc:" + soft.get());
輸出結果:before gc :abc
after gc: null
因此可以看出,軟引用和弱引用被gc回收的過程是一致的,但是最後到底會不會回收掉該對象,要分情況。對於軟引用來說,如果內存不足的情況下才會回收掉;對於弱引用來說,只要gc準備回收該弱引用對象,就會被立即釋放掉。
6.4 虛引用(PhantomReference)
“虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定對象的生命週期。如果一個對象僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收。虛引用主要用來跟蹤對象被垃圾回收的活動。
虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用隊列(ReferenceQueue)聯合使用。當垃 圾回收器準備回收一個對象時,如果發現它還有虛引用,就會在回收對象的內存之前,把這個虛引用加入到與之關聯的引用隊列中。程序可以通過判斷引用隊列中是 否已經加入了虛引用,來了解被引用的對象是否將要被垃圾回收。程序如果發現某個虛引用已經被加入到引用隊列,那麼就可以在所引用的對象的內存被回收之前採取必要的行動。 建立虛引用之後通過get方法返回結果始終爲null。
四種引用類型的聲明週期如下: