java集合中ArrayList與LinkedList比較
作者 codercjg 在 9 一月 2015, 11:20 下午
ArrayList和LinkedList共同點:都實現了List接口,用來表示動態數組的功能,都是線程不安全的。
不同點:1)實現方式不同,ArrayList內部用數組實現,LinkedList用鏈表實現。所以表中間插入元素時ArrayList需要移動元素,而LinkedList不需要。2)應用場景不同,ArrayList適合隨機查找,而LinkedList適合經常需要插入和刪除元素的場景。3)LinkedList比ArrayList多了addFirst(),addLast()方法4)ArrayList支持隨機查找,LinkedList不支持
下面通過一個例子來比較兩種實現方式的插入和遍歷所需要的時間:
import
java.util.ArrayList;
import
java.util.Iterator;
import
java.util.LinkedList;
public
class
ListSample {
public
static
void
main(String[] args) {
int
circle =
200000
;
ArrayList<String> arrayList =
new
ArrayList<String>(circle);
LinkedList<String> linkedList =
new
LinkedList<String>();
System.out.println(
"比較插入在頭部元素的時間:"
);
long
time = System.currentTimeMillis();
for
(
int
i =
0
; i < circle; i++) {
arrayList.add(
0
, String.valueOf(i));
}
System.out.println(
"ArrayList:"
- (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
for
(
int
i =
0
; i < circle; i++) {
linkedList.addFirst(String.valueOf(i));
}
System.out.println(
"LinkedList:"
- (System.currentTimeMillis() - time));
System.out.println();
System.out.println(
"比較for循環方式遍歷的時間:"
);
time = System.currentTimeMillis();
for
(
int
i =
0
; i < circle; i++) {
arrayList.get(i);
}
System.out.println(
"ArrayList:"
- (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
for
(
int
i =
0
; i < circle; i++) {
linkedList.get(i);
}
System.out.println(
"LinkedList:"
- (System.currentTimeMillis() - time));
System.out.println();
System.out.println(
"比較foreach方式遍歷的時間:"
);
time = System.currentTimeMillis();
for
(String str: arrayList){
}
System.out.println(
"ArrayList:"
- (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
for
(String str: linkedList){
}
System.out.println(
"LinkedList:"
- (System.currentTimeMillis() - time));
System.out.println();
System.out.println(
"比較迭代器方式遍歷的時間:"
);
time = System.currentTimeMillis();
Iterator it = linkedList.iterator();
while
(it.hasNext()){
it.next();
}
System.out.println(
"ArrayList:"
- (System.currentTimeMillis() - time));
time = System.currentTimeMillis();
it = linkedList.iterator();
while
(it.hasNext()){
it.next();
}
System.out.println(
"LinkedList:"
- (System.currentTimeMillis() - time));
}
}
運行結果:比較插入在頭部元素的時間:ArrayList:16829LinkedList:234
比較for循環方式遍歷的時間:ArrayList:0LinkedList:110375
比較foreach方式遍歷的時間:ArrayList:31LinkedList:16
比較迭代器方式遍歷的時間:ArrayList:15LinkedList:16
在ArrayList頭部插入元素竟然花了16829ms, 因為ArrayList內部是數組,所以會出現大量移動元素操作.而LinkedList內部是鏈表,所以很容易就在表頭插入新的結點。使用LinkedList for循環遍歷方式竟然花了110375ms, 因為LinkedList不支持隨機查找,每次都會從表頭開始查找,而ArrayList支持隨機查找。
結論:ArrayList適用于需要隨機查找的場景,LinkedList適用于需要大量插入和刪除元素操作的場景。
java集合ArrayList和Vector
作者 codercjg 在 9 一月 2015, 9:38 下午
ArrayList實現了List接口,是最常用的集合類。它的內部是用數組實現,使用時會根據包含的元素數目自動擴容。至于Vector,功能與實現方式都和ArrayList一樣的,但效率比ArrayLsit略低,因為Vector是ArrayList的同步版本,它的方法前大部分都加了Sychronized, 是多線程安全的,而ArrayList不是。
ArrayList用法比較簡單,可以把它當作容量沒有限制的數組來用,但也會有一些容易忽略的小細節。1)創建ArrayList對象時傳入一個合適的容量,有助于提高效率,因為默認容量是10,如果超出了就需要擴容,這個比較耗cpu時間2)add(), get(), remove()方法中的索引都不能超過ArrayList.size(), 否則運行時會拋越界異常3)contains()方法判斷是否包含某元素是默認調用元素的equals()方法,如果沒有則調用元素的基類Object的equals()方法,該方法默認比較兩個對象的引用,如果想比較對象的內容,需要在元素中重寫equals()方法
下面通過代碼來說明:
Product.java
public
class
Product {
private
int
id;
private
String name;
private
int
price;
public
Product(
int
id, String name,
int
price) {
super
();
this
.id = id;
this
.name = name;
this
.price = price;
}
public
int
getId() {
return
id;
}
public
void
setId(
int
id) {
this
.id = id;
}
public
String getName() {
return
name;
}
public
void
setName(String name) {
this
.name = name;
}
public
int
getPrice() {
return
price;
}
public
void
setPrice(
int
price) {
this
.price = price;
}
@Override
public
int
hashCode() {
final
int
prime =
31
;
int
result =
1
;
result = prime * result + id;
result = prime * result + ((name ==
null
) ?
0
: name.hashCode());
result = prime * result + price;
return
result;
}
@Override
public
boolean
equals(Object obj) {
if
(
this
== obj)
return
true
;
if
(obj ==
null
)
return
false
;
if
(getClass() != obj.getClass())
return
false
;
Product other = (Product) obj;
if
(id != other.id)
return
false
;
if
(name ==
null
) {
if
(other.name !=
null
)
return
false
;
}
else
if
(!name.equals(other.name))
return
false
;
if
(price != other.price)
return
false
;
return
true
;
}
@Override
public
String toString() {
return
"Product [id="
- id +
", name=" - name +
", price=" - price +
"]"
;
}
}
ArrayListSample.java
import
java.util.ArrayList;
import
java.util.Enumeration;
import
java.util.Iterator;
import
java.util.List;
import
java.util.Vector;
public
class
ArrayListSample {
public
static
void
main(String[] args) {
Product p1 =
new
Product(
1
,
"java"
,
40
);
Product q1 =
new
Product(
1
,
"java"
,
40
);
Product p2 =
new
Product(
2
,
"C++"
,
50
);
Product p3 =
new
Product(
3
,
"php"
,
60
);
Product p4 =
new
Product(
4
,
"pthyon"
,
70
);
Product q4 =
new
Product(
4
,
"pthyon"
,
70
);
//1. ArrayList內部是用數組實現的,容量默認為10,當容量不夠時會擴容,并把原來數組中內容
// 拷貝到新創建的數組中,出于效率考慮,創建ArrayList對象時盡量給它設置一個合適的容量
List<Product> list =
new
ArrayList<Product>();
list.add(p1);
list.add(p2);
list.add(p3);
list.add(
2
, p4);
list.add(
2
, q4);
// 可插入元素
// 2. add() get() remove()方法中的索引不能超過list.size(),否則會拋出越界異常
// list.add(10, p4);
// list.remove(10);
// System.out.println(list.get(100));
// 3. 打印ArrayList對象時,會調用ArrayList的toString()方法,在該方法內部會調用
// Product的toString()方法,如果Product沒有覆蓋Object的toString()方法,那么
// 會調用基類Object的toString()方法,將打印類似Product@77158a類名@對象hashCode的信息
System.out.println(list);
// 4. 調用contains()方法判斷是否包含某個對象時,contains()會調用equals()方法進行判斷,
// 而Object類中quals()方法默認是比較對象的引用,如果想根據內容判斷,可重新Product的equals()
// 和hashCode()方法
System.out.println(list.contains(p1));
// true
System.out.println(list.contains(q1));
// 重寫equals()和hashCode()方法前為false, 重寫后為true
// ArrayList的三種遍歷方法
System.out.println(
"for循環遍歷"
);
int
size = list.size();
for
(
int
i=
0
; i<size; i++){
System.out.println(list.get(i));
}
System.out.println(
"foreach遍歷"
);
for
(Product p:list){
System.out.println(p);
}
System.out.println(
"迭代器遍歷"
);
Iterator<Product> it = list.iterator();
while
(it.hasNext()){
System.out.println(it.next());
}
//實際測得,for循環方式遍歷最快,然后是迭代器方式,最后是foreach方式
Vector <Product> v =
new
Vector<Product>();
v.add(p1);
v.add(p2);
v.add(p3);
v.add(
2
, p4);
v.add(
2
, q4);
// Vector除了可用ArrayList的三種方式遍歷外,還可用下面的Enumeration方式
System.out.println(
"Enumeration遍歷"
);
Enumeration<Product> s= v.elements();
while
(s.hasMoreElements()){
System.out.println(s.nextElement());
}
}
}
執行結果:[Product [id=1, name=java, price=40], Product [id=2, name=C++, price=50], Product [id=4, name=pthyon, price=70], Product [id=4, name=pthyon, price=70], Product [id=3, name=php, price=60]]truetruefor循環遍歷Product [id=1, name=java, price=40]Product [id=2, name=C++, price=50]Product [id=4, name=pthyon, price=70]Product [id=4, name=pthyon, price=70]Product [id=3, name=php, price=60]foreach遍歷Product [id=1, name=java, price=40]Product [id=2, name=C++, price=50]Product [id=4, name=pthyon, price=70]Product [id=4, name=pthyon, price=70]Product [id=3, name=php, price=60]迭代器遍歷Product [id=1, name=java, price=40]Product [id=2, name=C++, price=50]Product [id=4, name=pthyon, price=70]Product [id=4, name=pthyon, price=70]Product [id=3, name=php, price=60]Enumeration遍歷Product [id=1, name=java, price=40]Product [id=2, name=C++, price=50]Product [id=4, name=pthyon, price=70]Product [id=4, name=pthyon, price=70]Product [id=3, name=php, price=60]
java中的static與final
作者 codercjg 在 8 一月 2015, 11:34 上午
static變量:非static變量告訴編譯器每創建一個對象都為該變量分配一份存儲空間。而static變量是告訴編譯器為該變量只分配一份存儲空間,和創建多少對象沒關系,甚至根本不需要創建對象。所以無論是類static成員變量還是方法中的static局部變量都只有一份存儲空間,它的值會累積。
static方法:調用非static方法時編譯器會把一個表示當前對象的this句柄作為參數傳入該方法,而static修飾方法時是告訴編譯器不要傳入參數this句柄,所以static方法中不能調用非靜態方法和引用類中的非靜態成員變量,因為他們需要傳入this。
static內部類:內部類默認需要持有創建它的外部類對象的this句柄,所以創建內部類對象前需要創建一個外部類對象。在內部類前加了static關鍵字后,就是告訴編譯器不需要這個句柄,所以創建該static內部類對象前不需要創建一個外部內對象。因為沒有外部內對象的this句柄,所以也不能使用與外部類對象相關的變量和方法。
總結:static 變量作用:告訴編譯器只為該變量分配一份存儲空間,和有多個對象沒關系,即使沒有對象也會分配一份存儲空間
static方法作用是:告訴編譯器調用該方法時不需要傳入this參數,那么自然不用創建對象了,當然在它里面也不能使用與對象關聯的類成員變量,和需要傳入this參數的非static方法了。
static內部類作用:創建內部類對象時,不需要創建外部內對象。
final修飾變量:告訴編譯器該變量只能被賦一次初值(編譯期或運行期都可以),之后它的值不能被改變,否則編譯器提示出錯。編譯器會保證final變量有初值,未指定初值需在構造函數中指定。
public
class
Test{
public
static
final
int
A=
1
;
//告訴編譯器只分配一份存儲空間,且A不能被修改。
//private final int a; // 沒有初值
private
final
int
a_1;
// 構造函數中賦初值
private
final
int
b =
1
;
private
int
c =
1
;
private
final
int
d = ++c;
//運行時賦賦初值
public
Test(){
a_1 =
1
;
}
public
void
change(
final
int
arg1){
//arg1 = 1; // 不能修改
//b = 1; // 不能修改
}
public
static
void
main(String[] args){
Test test =
new
Test();
test.change(
1
);
}
}
上面的注釋掉的代碼行編譯時會出錯。
final方法:1.出于設計的考慮把方法鎖定,告訴編譯器該方法不能被繼承它的類修改它的含義,所以不能被覆蓋2.早期jdk版本出于效率的考慮,用于告訴編譯器把該方法作為一個內聯函數,在調用時用代碼展開,省去通過壓棧傳入參數后跳轉到指定代碼執行,然后跳回并清理棧中的參數的開銷。但j2se5/6之后jvm會自動處理效率問題。所以除非明確禁止覆蓋時,才設置方法為final的。類中所有的private方法都隱式指定為final的,由于無法取用private方法,所以也就無法覆蓋它。
final類:告訴編譯器該類不能被修改,不允許被繼承
String類的實現和陷阱
作者 codercjg 在 8 一月 2015, 12:31 上午
String中的數據結構:
public
final
class
String
implements
java.io.Serializable, Comparable<String>, CharSequence
{
/** The value is used for character storage. */
private
final
char
value[];
/** The offset is the first index of the storage that is used. */
private
final
int
offset;
/** The count is the number of characters in the String. */
private
final
int
count;
}
從String源碼中可知String對象用字符數組value存儲字符串常量,有效內容為value[offset~count-1];String中每一個看似會改變其內容的方法其實是返回了一個新new的String對象,字符串則存在String對象中新new的字符數組里。
當兩個String對象擁有相同的值時,會引用字符串常量池中的同一個拷貝,如下所示
String str1=
"abc"
;
String str2=
"abc"
;
String str3=
new
String(
"abc"
);
System.out.println(str1==str2);
// true
System.out.println(str1==str3);
// false;
System.out.println(str1==str3.intern());
// true
因為”abc”是一個String對象,str1和str2都指向String對象”abc”;而String3指向了另一個新new的對象,而這個對象的字符數組指向{‘a’,'b’,'c’}
String中的方法:String.toString():返回字符串一個打印對象地址的陷阱:
public
class
A{
public
String toString(){
return
""
this
;
}
}
調用類A中的toString()方法時會得到一個異常,因為”"+this時,編譯器會調用toString()方法,這樣就構成了一個沒有出口的遞歸。如果要打印對象的地址,應該調用Object.toString(), super.toString().
如果經常要進行類似str1+”sss”+”str2″這樣的操作,應該選用StringBuilder.append()方法。至于StringBuffer, 和StringBuilder功能類似,只是用于多線程的同步版本。如果String經常要用findString()查找的話,那么可以選擇StringTokenizer,這個效率更高。
gravity layout_gravity和layout_weight
作者 codercjg 在 6 一月 2015, 4:56 下午
linearlayout中三個參數比較搞啊gravity:view中文字的對齊方式layout_gravity:view在linearlayout中的位置layout_weight:線性布局所占寬度減去所有子控件layout_width(orition=horizontal)或者layout_height(orition=vertical)后剩余空間所占的比例,默認為0如下面的例子:剩余空間=父控件寬度-三個TextView寬度其中父控件為線性布局,占滿整個屏幕。設屏幕寬度為W所以寬度W,三個TextView的寬度為W1,W2,W3, 剩余寬度為SW,則SW= W-W1-W2-W3又因為W1=W2=W3=0所以剩余空間SW=W屏幕的寬度其中三個TextView的layout_weight比例關系為1:1:1所以他們的寬度都為1/3屏幕寬度
<?
xml
version
=
"1.0"
encoding
=
"utf-8"
?>
<
LinearLayout
xmlns:android
=
"http://schemas.android.com/apk/res/android"
android:layout_width
"match_parent"
android:layout_height
"match_parent"
android:orientation
"horizontal"
<
TextView
android:layout_width
"0dp"
android:layout_height
"wrap_content"
android:layout_weight
"1"
android:text
"1"
android:background
"#FF0000"
/>
<
TextView
android:layout_width
"0dp"
android:layout_height
"wrap_content"
android:layout_weight
"1"
android:text
"1"
android:background
"#00FF00"
android:gravity
"center"
/>
<
TextView
android:layout_width
"0dp"
android:layout_height
"wrap_content"
android:layout_weight
"1"
android:text
"1"
android:background
"#0000FF"
android:layout_gravity
"center"
/>
</
LinearLayout