# 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}]