# Java - Collections(List & Set)
###### tags: `Java` `Basic Java`
## List - ArrayList
幾個特性:
1. 有排序
2. 可塞重複值
3. 可利用索引尋找元素
### 1. 添加元素 - add()
用的方法是add(),但是有以下注意點
1. 若要在List當中添加元素,則add()永遠返回true。(因為List允許元素重複)
2. 若要在Set當中添加元素,元素存在回返回false,元素不存在才會返回true。(因為Set當中不允許重複值出現)
### 2. 獲取元素 - get()
此為子類別list的方法(Collection沒有)
參數放索引值
### 3. 清空 - clear()
### 4. 移除元素 - remove()
1. 參數裡面放的是**物件,不是索引**
2. 刪除成功(元素存在)返回true,刪除失敗(元素不存在)返回false
3. 此方法只會移除第一次出現的元素,又因為刪除元素後list會自動將後續元素往前補,故若要將某重複元素全部都刪除乾淨,需要從後面往前面開始刪(詳見Iteration)
### 5. 是否包含物件 - contains()
底層source code使用equals()來判斷物件是否包含,**故如果需要判斷自定義的物件,需要重寫contains的方法**
舉例說明:
我們先創建一個student的java bean
```
public class Student {
private String name;
private int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
```
然後使用collection進行添加
```
public static void main(String[] args) {
Collection<Student> collections = new ArrayList<>();
Student stu1 = new Student("John", 23);
Student stu2 = new Student("Max", 24);
Student stu3 = new Student("Bob", 25);
collections.add(stu1);
collections.add(stu2);
collections.add(stu3);
Student stu4 = new Student("John", 23);
System.out.println(collections.contains(stu4));
}
```
**此時返回結果為false**
因為原本的equals()的方法是從Object Class來的,比較的是物件的地址是否相同,而並非比較裡面的值是否相同。而stu1跟stu4因為是不同的物件,所以具有不同的地址
**但如果我們需要將stu1跟stu4視為重複的物件,讓contains返回true,則需要在student的java bean中改寫equals()方法**
```
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
```
ps. 這段用IDEA直接生成即可(右鍵generate > equals() and hashCode()),重點不是code本身而是這個重寫的概念
改寫完之後再次執行System.out.println(collections.contains(stu4));就會返回true了
### 6. 是否為空 - isEmpty()
底層為判斷size()是否為0
### 7. 集合長度 - size()
### 8. 轉成陣列 - toArray()
建議用Object[]去接,因為可能會含有其他類型的data
### 9. Iteration:
**注意點:普通for loop只能遍歷有索引的List,不能遍歷沒有索引的Set**
#### 1. Iterator:
默認索引值為0
兩個常用方法:
hasNext() -> 判斷目前的位置是否有元素,有true無false
next() -> 取得目前位置的元素,並將iterator移到下一個索引位置
```
public static void main(String[] args) {
Collection<Integer> coll = new ArrayList<>();
coll.add(0);
coll.add(1);
coll.add(2);
coll.add(3);
coll.add(4);
coll.add(5);
coll.add(6);
coll.add(7);
coll.add(8);
coll.add(9);
// 創建指針
Iterator<Integer> it = coll.iterator();
// 用hasNext當作循環的判斷
// 若有指針返回true代表有東西就繼續跑
// 返回false代表跑完了就結束循環
while(it.hasNext()) {
// 獲取元素,賦值給變數後,將指針往後移
int number = it.next();
System.out.print(number + "\t");
}
}
```
細節:
1. 指針遍歷後不會自動復位,要重新遍歷的話需要重新new一個新的
2. 指針跑到最後若繼續使用next()就會有NoSuchElementException (不是out of bound,因為不是用索引遍歷)
3. **循環中只能使用一個next(),若用兩次以上,因為移動多次所以一定會超過集合的範圍,就會出現NoSuchElementException**
4. 使用指針遍歷時無法使用collection的add()或是remove() -> ConcurrentModificationException
要刪除元素只能使用iterator的remove()
#### 2. for each (只適用於單集合)
承上點範例
```
for (int i: coll) {
// i = 100;
}
System.out.print(coll);
```
1. int的部分可以換成其他type,只要跟集合的type一致即可
2. 即便對循環中的變數做賦值,也不會改變原集合的內容。
#### 3. Lambda Expression
先來看一下inner class的表達方式
```
coll.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
```
> 輸出結果為
> 0
> 1
2
3
4
5
6
7
8
9
底層source code原理:
遍歷集合後把元素逐一交給accept的方法,然後透過我們的改寫把元素print出來
正確的lambda expression
```
coll.forEach(integer -> System.out.println(integer));
```
#### 4. 如何選擇???
1. 如果在遍歷過程中需要刪除元素,請用Iterator
2. 如果只需要遍歷元素,用for each或lambda expression即可
## List - LinkedList
幾個特點:
1. 底層為**雙向鏈接串列**
2. 查詢各元素的速度較慢(因為一定要從頭開始查)
3. 但對於頭尾元素的操作(增加刪除)具有優勢
因為對頭尾元素的操作有優勢,所以有些特別的API能配合使用:
* addFirst() == push() / offer()
* addLast() == pop() / poll()
* getFirst()
* getLast()
* removeFirst()
* removeLast()
**通常的應用場景:Stack與Queue**
### 1. Stack
```
LinkedList<String> stack = new LinkedList<>();
stack.addFirst("First");
stack.addFirst("Second");
stack.push("Third");
stack.push("Forth");
System.out.println(stack);
```
> [Forth, Third, Second, First]
```
stack.removeFirst();
System.out.println(stack);
```
> [Third, Second, First]
### 2. Queue
```
LinkedList<String> queue = new LinkedList<>();
queue.addLast("First");
queue.addLast("Second");
queue.offer("Third");
queue.offer("Forth");
System.out.println(queue);
```
> [First, Second, Third, Forth]
```
queue.removeFirst();
System.out.println(queue);
```
> [Second, Third, Forth]
## Set:
幾個特點:
1. 沒有排序(LinkedHashSet TreeSet除外)
3. 不能放重複(不能放重複物件)
4. 沒有索引值可以查詢
**若要讓set任為兩個一樣內容的物件是重複物件,則需要改寫hasCode()與equals()的方法(跟底層原始碼有關)**
舉例說明:
我們先建立一個學生類別的標準java bean,並順便改寫toString方法
```
static class Student {
private String name;
private int age;
private char sex;
public Student() {
}
public Student(String name, int age, char sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
```
建立hashSet之後放入學生物件
```
public static void main(String[] args) {
Set<Student> studentSet = new HashSet<>();
Student s1 = new Student("JoJo", 50, 'M');
Student s2 = new Student("JoJo", 50, 'M');
Student s3 = new Student("GaGa", 40, 'F');
Student s4 = new Student("Zaza", 35, 'M');
studentSet.add(s1);
studentSet.add(s2);
studentSet.add(s3);
studentSet.add(s4);
System.out.println(studentSet);
}
```
這時候print出來會發現有四個學生物件,但其實裡面s1跟s2的內容是一樣的,Java還是會把它當作不同的物件(因為reference不同)
要讓Java判斷s1與s2是同一個物件,就要進行改寫
```
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && sex == student.sex && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
```
改寫完之後再print一次,就只剩下3個學生物件了(重複物件已被排除)
> [Student{name='GaGa', age=40, sex=F}, Student{name='JoJo', age=50, sex=M}, Student{name='Zaza', age=35, sex=M}]