第4章 集合框架
第4章 集合框架
為什么需要集合框架
我們在存儲大量的數據的時候,首先想到的是數組;但是數組存在的問題: ①大小是固定的,受限②類型是固定。但是在項目開發過程中我們存儲的數據大部分情況都不是固定的數量。存儲公司的ERP的會員數,不是固定的,存儲公司的供應商,這些都不是固定的數量。如何存儲此類數據,java提供了一整套集合框架。
java提供的集合框架
為了提高編程的效率,java在jdk中提供一個包: java.util.*,該包下提供一系列的接口和實現類。用來存儲大量的數據。整體的繼承關系圖:
總結如下:
①Map接口產生Collection接口,Map接口可以理解為跟接口
②Map是一套體系,存儲的映射關系: key—–value的映射
③Collection接口有兩個直接子接口: List和Set
④List系列存儲有序可以重復的數據
⑤Set系列存儲的無序不允許重復的數據
⑥兩個比較器: Comparable和Comparator
⑦兩個工具類: Collections和Arrays
常用的集合接口和實現類
List接口
List存儲的有序允許重復的數據,線性表; List的常用方法;
public interface List<E> extends Collection<E>Collection接口常用方法
| boolean | add(E e) 確保此集合包含指定的元素(可選操作)。 |
| boolean | addAll(Collection c) 將指定集合中的所有元素添加到此集合(可選操作)。 |
| void | clear() 從此集合中刪除所有元素(可選操作)。 |
| boolean | contains(Object o) 如果此集合包含指定的元素,則返回 true 。 |
| boolean | containsAll(Collection c) 如果此集合包含指定 集合中的所有元素,則返回true。 |
| boolean | equals(Object o) 將指定的對象與此集合進行比較以獲得相等性。 |
| int | hashCode() 返回此集合的哈希碼值。 |
| boolean | isEmpty() 如果此集合不包含元素,則返回 true 。 |
| Iterator | iterator() 返回此集合中的元素的迭代器。 |
| default Stream | parallelStream() 返回可能并行的 Stream與此集合作為其來源。 |
| boolean | remove(Object o) 從該集合中刪除指定元素的單個實例(如果存在)(可選操作)。 |
| boolean | removeAll(Collection c) 刪除指定集合中包含的所有此集合的元素(可選操作)。 |
| default boolean | removeIf(Predicate filter) 刪除滿足給定謂詞的此集合的所有元素。 |
| boolean | retainAll(Collection c) 僅保留此集合中包含在指定集合中的元素(可選操作)。 |
| int | size() 返回此集合中的元素數。 |
| default Spliterator | spliterator() 創建一個Spliterator在這個集合中的元素。 |
| default Stream | stream() 返回以此集合作為源的順序 Stream 。 |
| Object[] | toArray() 返回一個包含此集合中所有元素的數組。 |
| T[] | toArray(T[] a) 返回包含此集合中所有元素的數組; 返回的數組的運行時類型是指定數組的運行時類型。 |
List子接口常用方法:
| boolean | add(E e) 將指定的元素追加到此列表的末尾(可選操作)。 |
| void | add(int index, E element) 將指定的元素插入此列表中的指定位置(可選操作)。 |
| boolean | addAll(Collection c) 按指定集合的迭代器(可選操作)返回的順序將指定集合中的所有元素附加到此列表的末尾。 |
| boolean | addAll(int index, Collection c) 將指定集合中的所有元素插入到此列表中的指定位置(可選操作)。 |
| void | clear() 從此列表中刪除所有元素(可選操作)。 |
| boolean | contains(Object o) 如果此列表包含指定的元素,則返回 true 。 |
| boolean | containsAll(Collection c) 如果此列表包含指定 集合的所有元素,則返回true。 |
| boolean | equals(Object o) 將指定的對象與此列表進行比較以獲得相等性。 |
| E | get(int index) 返回此列表中指定位置的元素。 |
| int | hashCode() 返回此列表的哈希碼值。 |
| int | indexOf(Object o) 返回此列表中指定元素的第一次出現的索引,如果此列表不包含元素,則返回-1。 |
| boolean | isEmpty() 如果此列表不包含元素,則返回 true 。 |
| Iterator | iterator() 以正確的順序返回該列表中的元素的迭代器。 |
| int | lastIndexOf(Object o) 返回此列表中指定元素的最后一次出現的索引,如果此列表不包含元素,則返回-1。 |
| ListIterator | listIterator() 返回列表中的列表迭代器(按適當的順序)。 |
| ListIterator | listIterator(int index) 從列表中的指定位置開始,返回列表中的元素(按正確順序)的列表迭代器。 |
| E | remove(int index) 刪除該列表中指定位置的元素(可選操作)。 |
| boolean | remove(Object o) 從列表中刪除指定元素的第一個出現(如果存在)(可選操作)。 |
| boolean | removeAll(Collection c) 從此列表中刪除包含在指定集合中的所有元素(可選操作)。 |
| default void | replaceAll(UnaryOperator operator) 將該列表的每個元素替換為將該運算符應用于該元素的結果。 |
| boolean | retainAll(Collection c) 僅保留此列表中包含在指定集合中的元素(可選操作)。 |
| E | set(int index, E element) 用指定的元素(可選操作)替換此列表中指定位置的元素。 |
| int | size() 返回此列表中的元素數。 |
| default void | sort(Comparator c) 使用隨附的 Comparator排序此列表來比較元素。 |
| default Spliterator | spliterator() 在此列表中的元素上創建一個Spliterator 。 |
| List | subList(int fromIndex, int toIndex) 返回此列表中指定的 fromIndex (含)和 toIndex之間的視圖。 |
| Object[] | toArray() 以正確的順序(從第一個到最后一個元素)返回一個包含此列表中所有元素的數組。 |
| T[] | toArray(T[] a) 以正確的順序返回一個包含此列表中所有元素的數組(從第一個到最后一個元素); 返回的數組的運行時類型是指定數組的運行時類型。 |
總結常用方法:
添加方法: add 、 add(int index, E element)、 addAll(Collection c)
刪除方法: remove(int index),remove(Object o)、removeAll(Collection c), clear()
修改方法: set(int index, E element), replaceAll(UnaryOperator operator)
查詢方法: get、indexOf(Object o)、lastIndexOf(Object o)、 subList(int fromIndex, int toIndex), size()
遍歷方法: iterator(), listIterator() , listIterator(int index)
判斷方法: isEmpty(), contains(Object o), containsAll(Collection c)
流操作方法: stream(), parallelStream()
排序方法: sort()
轉換方法: toArray() 、toArray(T[] a)
ArrayList用法
ArrayList底層是用數組存儲的,默認長度10, 數組類型是Object。動態數組,可變數組。
package ch005;import java.util.ArrayList; import java.util.Iterator; import java.util.List;public class Demo1 {public static void main(String[] args) {List list = new ArrayList();list.add("張無忌");list.add("周芷若");list.add("趙敏");list.add("小昭");list.add("殷離");System.out.println("size:"+ list.size());System.out.println("是否為空:"+ list.isEmpty());System.out.println("是否包括(小昭):"+list.contains("小昭"));System.out.println("獲取所有的女生:"+list.subList(1, 5));Object [] objs = list.toArray();System.out.print("轉換對象數組:");for(Object o: objs) {System.out.print(o+",");}System.out.println("修改元素:"+list.set(1, "滅絕師太"));System.out.println("修改之后:"+list);//1.通過for遍歷System.out.print("遍歷元素:");for(int i=0 ;i<list.size(); i++) {System.out.print(list.get(i)+",");}System.out.println("\n使用迭代器遍歷:");Iterator it = list.iterator();while(it.hasNext()) {//注意的問題: 遍歷的時候不允許添加或刪除元素,//否則會出現并發修改異常ConcurrentModificationExceptionlist.add("xx");System.out.println(it.next());}System.out.println("\n刪除元素:"+list.remove(0));System.out.println("刪除元素:"+list.remove("周芷若"));System.out.println("刪除兩個元素之后:"+list.size());}}創建ArrayList的說明:
/*** The array buffer into which the elements of the ArrayList are stored.* The capacity of the ArrayList is the length of this array buffer. Any* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA* will be expanded to DEFAULT_CAPACITY when the first element is added.*/ transient Object[] elementData; // non-private to simplify nested class accesselementData是ArrayList存儲數據的緩沖區,ArrayList的容量就是數組緩沖區的長度,當第一次添加元素的時候,空的ArrayList會被擴容到長度為10。
LinkedList
底層基于鏈表存儲的, ArrayList是數組,連續的內存空間;而鏈表不是連續空間。鏈表的每一個節點不但要存儲數據還要存儲上下元素的位置。鏈表相當于小朋友手拉手組成一個隊列,左手存儲的上一個元素的位置,右手存儲下一個元素的位置,小朋友本身是元素存數據。
常用方法:
| boolean | add(E e) 將指定的元素追加到此列表的末尾。 |
| void | add(int index, E element) 在此列表中的指定位置插入指定的元素。 |
| boolean | addAll(Collection c) 按照指定集合的迭代器返回的順序將指定集合中的所有元素追加到此列表的末尾。 |
| boolean | addAll(int index, Collection c) 將指定集合中的所有元素插入到此列表中,從指定的位置開始。 |
| void | addFirst(E e) 在該列表開頭插入指定的元素。 |
| void | addLast(E e) 將指定的元素追加到此列表的末尾。 |
| void | clear() 從列表中刪除所有元素。 |
| Object | clone() 返回此 LinkedList的淺版本。 |
| boolean | contains(Object o) 如果此列表包含指定的元素,則返回 true 。 |
| Iterator | descendingIterator() 以相反的順序返回此deque中的元素的迭代器。 |
| E | element() 檢索但不刪除此列表的頭(第一個元素)。 |
| E | get(int index) 返回此列表中指定位置的元素。 |
| E | getFirst() 返回此列表中的第一個元素。 |
| E | getLast() 返回此列表中的最后一個元素。 |
| int | indexOf(Object o) 返回此列表中指定元素的第一次出現的索引,如果此列表不包含元素,則返回-1。 |
| int | lastIndexOf(Object o) 返回此列表中指定元素的最后一次出現的索引,如果此列表不包含元素,則返回-1。 |
| ListIterator | listIterator(int index) 從列表中的指定位置開始,返回此列表中元素的列表迭代器(按適當的順序)。 |
| boolean | offer(E e) 將指定的元素添加為此列表的尾部(最后一個元素)。 |
| boolean | offerFirst(E e) 在此列表的前面插入指定的元素。 |
| boolean | offerLast(E e) 在該列表的末尾插入指定的元素。 |
| E | peek() 檢索但不刪除此列表的頭(第一個元素)。 |
| E | peekFirst() 檢索但不刪除此列表的第一個元素,如果此列表為空,則返回 null 。 |
| E | peekLast() 檢索但不刪除此列表的最后一個元素,如果此列表為空,則返回 null 。 |
| E | poll() 檢索并刪除此列表的頭(第一個元素)。 |
| E | pollFirst() 檢索并刪除此列表的第一個元素,如果此列表為空,則返回 null 。 |
| E | pollLast() 檢索并刪除此列表的最后一個元素,如果此列表為空,則返回 null 。 |
| E | pop() 從此列表表示的堆棧中彈出一個元素。 |
| void | push(E e) 將元素推送到由此列表表示的堆棧上。 |
| E | remove() 檢索并刪除此列表的頭(第一個元素)。 |
| E | remove(int index) 刪除該列表中指定位置的元素。 |
| boolean | remove(Object o) 從列表中刪除指定元素的第一個出現(如果存在)。 |
| E | removeFirst() 從此列表中刪除并返回第一個元素。 |
| boolean | removeFirstOccurrence(Object o) 刪除此列表中指定元素的第一個出現(從頭到尾遍歷列表時)。 |
| E | removeLast() 從此列表中刪除并返回最后一個元素。 |
| boolean | removeLastOccurrence(Object o) 刪除此列表中指定元素的最后一次出現(從頭到尾遍歷列表時)。 |
| E | set(int index, E element) 用指定的元素替換此列表中指定位置的元素。 |
| int | size() 返回此列表中的元素數。 |
| Spliterator | spliterator() 在此列表中的元素上創建*late-binding和故障快速* Spliterator 。 |
| Object[] | toArray() 以正確的順序(從第一個到最后一個元素)返回一個包含此列表中所有元素的數組。 |
| T[] | toArray(T[] a) 以正確的順序返回一個包含此列表中所有元素的數組(從第一個到最后一個元素); 返回的數組的運行時類型是指定數組的運行時類型。 |
案例:
public static void main(String[] args) {LinkedList list = new LinkedList();list.add(5);list.add(6);list.add(7);//帖子置頂list.addFirst(1);list.addLast(8);System.out.println(list);System.out.println("First=="+list.getFirst());System.out.println("Last==="+list.getLast());list.removeFirst();list.removeLast();System.out.println("刪除之后: "+list);//Deque 隊列,火車站買票排隊就是一個隊列list.push(10);list.push(11);System.out.println("push之后:"+list);Object first = list.poll();//檢索并刪除此列表的頭(第一個元素) 買票之后走人System.out.println("poll的元素:"+first);System.out.println("poll之后:"+list); }LinkedList源碼分析:
存儲數據的對象: 靜態內部類,只能在當前類使用
private static class Node<E> {E item; //數據Node<E> next;//下一個節點的引用Node<E> prev;//上一個節點的引用Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;} }添加元素:
public boolean add(E e) {linkLast(e);return true; }/*** Links e as last element.連接一個數據作為最后的元素*/ void linkLast(E e) {final Node<E> l = last; //獲取最后的元素final Node<E> newNode = new Node<>(l, e, null);//創建節點last = newNode;//把新節點賦值lastif (l == null)first = newNode; //把新節點賦值first第一個elsel.next = newNode;//把最后一個節點的next引用,指向到新節點,組成隊列size++;//整個隊列的元素加 1 modCount++; }上機練習代碼
練習一、練習二、
①實體類:
package ch005;public class Student {private int no;private String name;private char sex;public int getNo() {return no;}public void setNo(int no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}@Overridepublic String toString() {return "Student [no=" + no + ", name=" + name + ", sex=" + sex + "]";}public Student(int no, String name, char sex) {super();this.no = no;this.name = name;this.sex = sex;}public Student() {super();} }②業務類
package ch005;import java.util.ArrayList; import java.util.List;public class StuMgr {// 包,存放的鑰匙,有錢包,有紙巾,有水筆,化妝品,手機,充電寶// 包: 紙巾包,只能存儲紙巾,取出比較方便private List<Student> list = new ArrayList<>();//添加方法public void addStudent(Student student) {list.add(student);}//查詢public Student findStudent(String name) {Student stu = null;for(Student s : list) {if(s.getName().equals(name)) {stu = s;//s是找到的對象,賦值給stubreak;}}return stu;}//顯示所有public void showAll() {System.out.println("學號\t姓名\t性別");for (int i = 0; i < list.size(); i++) {Student s = list.get(i);System.out.println(s.getNo() + "\t" + s.getName() + "\t" + s.getSex());}} }③測試類
public static void main(String[] args) {System.out.println("---------歡迎使用學生管理系統-------------");Scanner input = new Scanner(System.in);StuMgr mgr = new StuMgr();while (true) {System.out.println("請選擇:1.添加學生 2.查詢學生 3.刪除學生 4.修改信息 5.退出系統");int no = input.nextInt();if (no == 1) {while (true) {System.out.print("請輸入學生學號:");int num = input.nextInt();System.out.print("請輸入學生姓名:");String name = input.next();System.out.print("請輸入學生性別:");char sex = input.next().charAt(0);Student s = new Student(num, name, sex);mgr.addStudent(s);System.out.print("是否繼續?");String str = input.next();if (str.equals("n")) {break;}}mgr.showAll();} else if (no == 2) {while(true) {System.out.print("請輸入您要查詢的學生姓名:");String name = input.next();Student student = mgr.findStudent(name);if (student != null) {System.out.println("查詢結果:" + student);}else {System.out.println(name +"不存在");}System.out.print("是否繼續?");String str = input.next();if (str.equals("n")) {break;}}} else if (no == 3) {} else if (no == 4) {} else {System.out.println("退出系統..");break;}}input.close(); }練習三、
package ch005;import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Task3 {public static void main(String[] args) {Student[] students = new Student[3];students[0] = new Student(1001,"張無忌",'男');students[1] = new Student(1002,"趙敏",'女');students[2] = new Student(1002,"周芷若",'女');List<Student> list = new ArrayList<>();for(int i=0; i<students.length; i++) {list.add(students[i]);}Student s = new Student(1003,"小昭",'女');list.add(s);System.out.println("查看List集合:");Iterator<Student> it = list.iterator();while(it.hasNext()) {Student sss = it.next();System.out.println(sss);}} }Set接口
set是Collection的子接口,特點是: 無序不允許重復。 List是一隊人,可以重復; Set 一堆人不能重復。常用實現類HashSet, TreeSet , LinkedHashSet(按照插入的順序排列)
HashSet用法
基于Hash算法實現的集合,Hash產生一個唯一的編碼,也可以高效的查詢數據; 此類實現Set接口,由哈希表(實際為HashMap實例)支持。 對集合的迭代次序不作任何保證; 特別是,它不能保證訂單在一段時間內保持不變。 這個類允許null元素。
常用方法:
package ch005;import java.util.HashSet; import java.util.Iterator; import java.util.Set;public class Demo2 {public static void main(String[] args) {Set set = new HashSet();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');set.add(s1);set.add(s2);set.add(s3);System.out.println("size:"+set.size());System.out.println("是否為空:"+set.isEmpty());Iterator it = set.iterator();while(it.hasNext()) {Student s = (Student) it.next();System.out.println(s);}} }如果指定存儲類型:
package ch005;import java.util.HashSet; import java.util.Iterator; import java.util.Set;public class Demo2 {public static void main(String[] args) {Set<Student> set = new HashSet<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');set.add(s1);set.add(s2);set.add(s3);System.out.println("size:"+set.size());System.out.println("是否為空:"+set.isEmpty());Iterator<Student> it = set.iterator();while(it.hasNext()) {Student s = it.next();System.out.println(s);}} }HashSet的底層實現:
public class HashSet<E> extends AbstractSet<E> implements Set<E>, Cloneable, java.io.Serializable{static final long serialVersionUID = -5024744406713321676L;//HashSet存儲容器private transient HashMap<E,Object> map;// Dummy value to associate with an Object in the backing Mapprivate static final Object PRESENT = new Object();/*** Constructs a new, empty set; the backing <tt>HashMap</tt> instance has* default initial capacity (16) and load factor (0.75).* HashSet底層借助HashMap存儲的,以key的方式存儲,value是一個虛擬對象 PRESENT*/public HashSet() {map = new HashMap<>();}//存儲數據的實現方式public boolean add(E e) {return map.put(e, PRESENT)==null;}....... }TreeSet用法
有序的Set, 按照的自然順序排列的集合,數值類型的 1,2,3,4; 字符類型的: A,B,C,D; 漢字不會排序。
public static void main(String[] args) {Set<String> set = new TreeSet<>();set.add("abc");set.add("bbc");set.add("abb");set.add("abd");System.out.println(set);Set<Integer> set1 = new TreeSet<>();set1.add(200);set1.add(100);set1.add(500);set1.add(50);System.out.println(set1);Iterator<Integer> it = set1.iterator();while(it.hasNext()) {Integer num = it.next();System.out.println(num);}}如果存儲的是自定義類型,會出現什么問題?
public static void main(String[] args) {Set<Student> set = new TreeSet<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');set.add(s1);set.add(s2);set.add(s3);System.out.println(set.size()); }出現如下異常:
Exception in thread "main" java.lang.ClassCastException: ch005.Student cannot be cast to java.lang.Comparableat java.util.TreeMap.compare(Unknown Source)at java.util.TreeMap.put(Unknown Source)at java.util.TreeSet.add(Unknown Source)at ch005.Demo2.main(Demo2.java:15)類型轉換異常: Student對象不能轉換成Comparable。為什么出現這個異常? TreeSet是有序的Set,有序是按照什么來進行排序的?
public TreeSet() {this(new TreeMap<E,Object>()); }要排序就要進行比較,TreeSet借助于TreeMap實現的,TreeMap的排序方式是兩個接口是的
①Comparable 表示可以比較的, Closeable可關閉的,對象具備的特征;同學之間可以比較身高、體重,
②Comparator 比較器: 借助于外部的尺度,進行比較; 對象之間不好比較: 貨車座位有序的,借助于車票的序號,電影院容器也是有序的;借助于座位號比較的。
使用Comparable比較器:
package ch005;public class Student implements Comparable<Student>{private Integer no;private String name;private char sex;public Integer getNo() {return no;}public void setNo(Integer no) {this.no = no;}public String getName() {return name;}public void setName(String name) {this.name = name;}public char getSex() {return sex;}public void setSex(char sex) {this.sex = sex;}@Overridepublic String toString() {return "Student [no=" + no + ", name=" + name + ", sex=" + sex + "]";}public Student(int no, String name, char sex) {super();this.no = no;this.name = name;this.sex = sex;}public Student() {super();}@Overridepublic int compareTo(Student o) {//用當前對象的某個屬性和穿過來的參數對象的屬性進行比較//比較的結果 > 0 表示當前對象的值大于參數//比較的結果 == 0 說明兩個對象比較的屬性相同//比較的結果 < 0 當前對象小于參數的數據int result = this.name.compareTo(o.getName());if(result == 0) {//如果名字相同再按照編號比較return this.getNo().compareTo(o.getNo());}System.out.println(name+"和"+o.getName()+"比較的結果:"+result);return result;}}測試類:
//a=97 b=98 c=99 d=100 e=101 f=102 g=103 h=104 i=105 j=106public static void main(String[] args) {Set<Student> set = new TreeSet<>();Student s1 = new Student(1001,"jack",'男');Student s2 = new Student(1002,"rose",'女');Student s3 = new Student(1003,"bluce",'女');Student s4 = new Student(1004,"rose",'女');set.add(s1);set.add(s2);set.add(s3);set.add(s4);System.out.println(set.size());for(Student s: set) {System.out.println(s);}}使用Comparator外部的比較器:胡潤百富榜,也稱為殺豬榜,就是一個外部的比較器,土豪之間不好意思比較誰的資產更多。
創建一個實體類,不實現Comparable接口,自身不具備比較性,要借助于第三方的比較器:
package ch005;public class Dept {private Integer deptNo;private String deptName;public Dept() {super();}public Dept(Integer deptNo, String deptName) {super();this.deptNo = deptNo;this.deptName = deptName;}public Integer getDeptNo() {return deptNo;}public void setDeptNo(Integer deptNo) {this.deptNo = deptNo;}public String getDeptName() {return deptName;}public void setDeptName(String deptName) {this.deptName = deptName;}@Overridepublic String toString() {return "Dept [deptNo=" + deptNo + ", deptName=" + deptName + "]";} }測試類:
package ch005;import java.util.Comparator; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.TreeSet;class MyComparator implements Comparator<Dept>{@Overridepublic int compare(Dept o1, Dept o2) {//1.按照名字順序比較//return o1.getDeptName().compareTo(o2.getDeptName());//2.按照名字的長度進行比較int x = o1.getDeptName().length();int y = o2.getDeptName().length();return (x < y) ? -1 : ((x == y) ? 0 : 1);}}public class Demo2 {//a=97 b=98 c=99 d=100 e=101 f=102 g=103 h=104 i=105 j=106public static void main(String[] args) {Set<Dept> set = new TreeSet<>( new MyComparator() );set.add(new Dept(1001,"sell"));set.add(new Dept(1002,"service"));set.add(new Dept(1003,"abc"));for (Dept dept : set) {System.out.println(dept);}} }自定義的比較器核心代碼只有一行: return o1.getDeptName().compareTo(o2.getDeptName()); 但是java語言是比較規范的語言,所以我們需要完整的定義一個類。就相當于自己吃飯我們要先種菜、收菜、自己加工等一整套流程。但是核心的業務是吃飯而已;如何優化整個流程: 點外賣。簡化了所有的流程,只關注核心業務。在jdk1.8中提供了Lambda表達式來簡化編碼。
簡化寫法: 使用Lambda表達式,
public static void main(String[] args) {Set<Dept> set = new TreeSet<>( (o1,o2) -> o1.getDeptName().compareTo(o2.getDeptName()) );set.add(new Dept(1001,"sell"));set.add(new Dept(1002,"service"));set.add(new Dept(1003,"abc"));for (Dept dept : set) {System.out.println(dept);} }只關注核心的一句話: 比較部門名稱,簡化了創建類的格式.
Map接口
map存儲的是鍵值對,key —value的映射關系; 所以稱為雙列集合,List就是單列集合; map的key不允許重復,value可以重復。Map接口的常見實現類: HashMap, TreeMap、 LinkedHashMap。需要注意的是都不是線程安全對象,線程安全的Map是Hashtable。在juc包中還有線程安全的實現類: ConcurrentHashMap.
HashMap用法
構造方法:
| HashMap() 構造一個空的 HashMap ,默認初始容量(16)和默認負載系數(0.75)。 |
| HashMap(int initialCapacity) 構造一個空的 HashMap具有指定的初始容量和默認負載因子(0.75)。 |
| HashMap(int initialCapacity, float loadFactor) 構造一個空的 HashMap具有指定的初始容量和負載因子。 |
| HashMap(Map m) 構造一個新的 HashMap與指定的相同的映射 Map 。 |
底層用的也是數組,數組的長度是16,負載因子0.75; 當容量達到75%的時候,容器要擴容。16*75%=12,意思是元素到達12的時候觸發容器擴容機制。
案例:
public static void main(String[] args) {Map<String,Student> map = new HashMap<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');//映射關系: 鹿晗-->關曉彤 黃曉明---楊穎 鄧超-->孫儷 劉強東---章澤天map.put("a", s1);map.put("b", s2);map.put("c", s3);System.out.println("size:"+map.size());System.out.println("是否包括(趙敏):"+map.containsKey("b"));System.out.println("是否包括對象:"+map.containsValue(s2));Student s = map.get("c");System.out.println("根據key獲取對象:"+s);//map的遍歷: 1.根據key獲取value,先獲取所有的key,然后再根據key獲取valueSet<String> keys = map.keySet();Iterator<String> it = keys.iterator();while(it.hasNext()) {String key = it.next();Student stn = map.get(key);System.out.println(key+"-->"+stn.getName());} }LinkedList用的靜態內部類Node存儲的,一個Node包括三部分; 類似Map一個元素包括兩部分: key-value, 如何管理方便: 可以借鑒LinkedList的存儲 機制,也創建一個類存儲key和value。這個類叫Entry: 取“條目”的意思。查看HashMap的源碼:
static class Node<K,V> implements Map.Entry<K,V> {final int hash;final K key;V value;Node<K,V> next;Node(int hash, K key, V value, Node<K,V> next) {this.hash = hash;this.key = key;this.value = value;this.next = next;} .......HashMap使用Node節點封裝key和value; 鍵值對,注意一個細節問題: next,表示下一個節點的引用;從中可以猜測HashMap中應該有一個單向鏈表結構。案例:
public static void main(String[] args) {Map<String,Student> map = new HashMap<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');map.put("a", s1);map.put("b", s2);map.put("c", s3);Set<Entry<String, Student>> entrySet = map.entrySet();for (Entry<String, Student> entry : entrySet) {String key = entry.getKey();Student value = entry.getValue();System.out.println(key+"---"+value);} }TreeMap
有序的Map,自定義排序參考TreeSet實現。
public static void main(String[] args) {Map<String,Student> map = new TreeMap<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');map.put("xxx", s1);map.put("b34b", s2);map.put("a12", s3);Set<Entry<String, Student>> entrySet = map.entrySet();for (Entry<String, Student> entry : entrySet) {String key = entry.getKey();Student value = entry.getValue();System.out.println(key+"---"+value);} }LinkedHashMap
按照添加的順序存儲元素,基于鏈表, 先放的元素作為前端節點;后續元素依次連接到前面元素。
public static void main(String[] args) {Map<String,Student> map = new LinkedHashMap<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1002,"周芷若",'女');map.put("a12", s3);map.put("b34b", s2);map.put("xxx", s1);Set<Entry<String, Student>> entrySet = map.entrySet();for (Entry<String, Student> entry : entrySet) {String key = entry.getKey();Student value = entry.getValue();System.out.println(key+"---"+value);} }jdk1.8對集合的增強
jdk 1.8對于集合各種操作簡化處理的學習
- 集合轉為stream
- 面向流的filter(有返回值true,false),排序sorted,map[mapToInt……,無返回值,把當前值映射成為另一個對象],distinct,distinctbyKey[重寫filter參數類型的方法 Predicate distinctByKey(Function<? super T, Object> keyExtractor)]
- 處理后的stream轉為集合collect collectors.toList() Collectors.toSet(),Collectors.toMap(Person::getName,Function.identity()) Collectors.groupingBy((f)->……/Person::getName)
- 處理后的流取第一個值findFirst optional類型 ispresent判斷
- 多個流的連接stream.of
- 操作集合的降維(降一維)flatMap(Function.identity)
stream用法
流的意思,可以向操作文件流一樣操作集合中的數據集; 也可以理解把集合中的數據當做數據庫。
Stream操作的三個步驟
- 創建stream
- 中間操作(過濾、map)
- 終止操作
首先創建備用的List集合:
List<String> stringList = new ArrayList<>(); stringList.add("ddd2"); stringList.add("aaa2"); stringList.add("bbb1"); stringList.add("aaa1"); stringList.add("bbb3"); stringList.add("ccc"); stringList.add("bbb2"); stringList.add("ddd1");Filter(過濾)
過濾通過一個predicate接口來過濾并只保留符合條件的元素,該操作屬于中間操作,所以我們可以在過濾后的結果來應用其他Stream操作(比如forEach)。forEach需要一個函數來對過濾后的元素依次執行。forEach是一個最終操作,所以我們不能在forEach之后來執行其他Stream操作。
public static void main(String[] args) {List<Student> list = new ArrayList<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1003,"周芷若",'女');list.add(s1);list.add(s2);list.add(s3);/** list.stream().filter(s -> s.getNo() == 1002) .forEach(System.out::println);*/List<Student> list2 = list.stream() //1.獲取stream.filter(s -> s.getNo() >= 1002)//2.過濾.collect(Collectors.toList());//3.轉換成Listfor (Student student : list2) {System.out.println(student);}}查找姓名長度為2的元素
public static void main(String[] args) {//查找姓名長度為2的元素List<Student> list = new ArrayList<>();Student s1 = new Student(1001,"張無忌",'男');Student s2 = new Student(1002,"趙敏",'女');Student s3 = new Student(1003,"周芷若",'女');list.add(s1);list.add(s2);list.add(s3);/*for (Student student : list) {if(student.getName().length() == 2) {System.out.println(student);}}*/Stream<Student> filter = list.stream().filter(s -> s.getName().length() ==2);List<Student> list2 = filter.collect(Collectors.toList());for(Student s: list2) {System.out.println(s);}}Sorted(排序)
排序是一個 中間操作,返回的是排序好后的 Stream。如果你不指定一個自定義的 Comparator 則會使用默認排序。
// 測試 Sort (排序) stringList.stream().sorted().filter((s) -> s.startsWith("a")).forEach(System.out::println);// aaa1 aaa2需要注意的是,排序只創建了一個排列好后的Stream,而不會影響原有的數據源,排序之后原數據stringCollection是不會被修改的:
System.out.println(stringList);// ddd2, aaa2, bbb1, aaa1, bbb3, ccc, bbb2, ddd1Map(映射)
中間操作 map 會將元素根據指定的 Function 接口來依次將元素轉成另外的對象。
下面的示例展示了將字符串轉換為大寫字符串。你也可以通過map來將對象轉換成其他類型,map返回的Stream類型是根據你map傳遞進去的函數的返回值決定的。
// 測試 Map 操作 stringList.stream().map(String::toUpperCase) .sorted((a, b) -> b.compareTo(a)) .forEach(System.out::println);// "DDD2", "DDD1", "CCC", "BBB3", "BBB2", "AAA2", "AAA1"Match(匹配)
Stream提供了多種匹配操作,允許檢測指定的Predicate是否匹配整個Stream。所有的匹配操作都是 最終操作 ,并返回一個 boolean 類型的值。
// 測試 Match (匹配)操作 boolean anyStartsWithA = stringList.stream().anyMatch((s) -> s.startsWith("a")); System.out.println(anyStartsWithA); // trueboolean allStartsWithA = stringList.stream().allMatch((s) -> s.startsWith("a")); System.out.println(allStartsWithA); // falseboolean noneStartsWithZ = stringList.stream().noneMatch((s) -> s.startsWith("z")); System.out.println(noneStartsWithZ); // trueCount(計數)
計數是一個 最終操作,返回Stream中元素的個數,返回值類型是 long。
//測試 Count (計數)操作 long startsWithB = stringList.stream().filter((s) -> s.startsWith("b")).count(); System.out.println(startsWithB); // 3Reduce(規約)
這是一個 最終操作 ,允許通過指定的函數來講stream中的多個元素規約為一個元素,規約后的結果是通過Optional 接口表示的:
//測試 Reduce (規約)操作 Optional<String> reduced = stringList.stream().sorted().reduce((s1, s2) -> s1 + "#" + s2); reduced.ifPresent(System.out::println);//aaa1#aaa2#bbb1#bbb2#bbb3#ccc#ddd1#ddd2這個方法的主要作用是把 Stream 元素組合起來。它提供一個起始值(種子),然后依照運算規則(BinaryOperator),和前面 Stream 的第一個、第二個、第 n 個元素組合。從這個意義上說,字符串拼接、數值的 sum、min、max、average 都是特殊的 reduce。例如 Stream 的 sum 就相當于Integer sum = integers.reduce(0, (a, b) -> a+b);也有沒有起始值的情況,這時會把 Stream 的前面兩個元素組合起來,返回的是 Optional。
// 字符串連接,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 求和,sumValue = 10, 無起始值 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 過濾,字符串連接,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F").filter(x -> x.compareTo("Z") > 0).reduce("", String::concat);上面代碼例如第一個示例的 reduce(),第一個參數(空白字符)即為起始值,第二個參數(String::concat)為 BinaryOperator。這類有起始值的 reduce() 都返回具體的對象。而對于第四個示例沒有起始值的 reduce(),由于可能沒有足夠的元素,返回的是 Optional,請留意這個區別。更多內容查看: IBM:Java 8 中的 Streams API 詳解
Parallel Streams(并行流)
前面提到過Stream有串行和并行兩種,串行Stream上的操作是在一個線程中依次完成,而并行Stream則是在多個線程上同時執行。
下面的例子展示了是如何通過并行Stream來提升性能:
首先我們創建一個沒有重復元素的大表:
int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) {UUID uuid = UUID.randomUUID();values.add(uuid.toString()); }我們分別用串行和并行兩種方式對其進行排序,最后看看所用時間的對比。
Sequential Sort(串行排序)
//串行排序 long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis));輸出結果:
1000000 sequential sort took: 709 ms//串行排序所用的時間Parallel Sort(并行排序)
//并行排序 long t0 = System.nanoTime();long count = values.parallelStream().sorted().count(); System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis));輸出結果:
1000000 parallel sort took: 475 ms//串行排序所用的時間上面兩個代碼幾乎是一樣的,但是并行版的快了 50% 左右,唯一需要做的改動就是將 stream() 改為parallelStream()。
ream上的操作是在一個線程中依次完成,而并行Stream則是在多個線程上同時執行。
下面的例子展示了是如何通過并行Stream來提升性能:
首先我們創建一個沒有重復元素的大表:
int max = 1000000; List<String> values = new ArrayList<>(max); for (int i = 0; i < max; i++) {UUID uuid = UUID.randomUUID();values.add(uuid.toString()); }我們分別用串行和并行兩種方式對其進行排序,最后看看所用時間的對比。
Sequential Sort(串行排序)
//串行排序 long t0 = System.nanoTime(); long count = values.stream().sorted().count(); System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("sequential sort took: %d ms", millis));輸出結果:
1000000 sequential sort took: 709 ms//串行排序所用的時間Parallel Sort(并行排序)
//并行排序 long t0 = System.nanoTime();long count = values.parallelStream().sorted().count(); System.out.println(count);long t1 = System.nanoTime();long millis = TimeUnit.NANOSECONDS.toMillis(t1 - t0); System.out.println(String.format("parallel sort took: %d ms", millis));輸出結果:
1000000 parallel sort took: 475 ms//串行排序所用的時間上面兩個代碼幾乎是一樣的,但是并行版的快了 50% 左右,唯一需要做的改動就是將 stream() 改為parallelStream()。
總結