JAVA集合和guava集合使用和原理解析
?
一:總體介紹:
Java集合框架介紹
?集合可以看作是一種容器,用來存儲對象信息。所有集合類都位于java.util包下,但支持多線程的集合類位于java.util.concurrent包下。
Java集合類主要由兩個根接口Collection和Map派生出來的,Collection派生出了三個子接口:List、Set、Queue(Java5新增的隊列),因此Java集合大致也可分成List、Set、Queue、Map四種接口體系,(注意:Map不是Collection的子接口)。
其中List代表了有序可重復(fù)集合,可直接根據(jù)元素的索引來訪問;Set代表無序不可重復(fù)集合,只能根據(jù)元素本身來訪問;Queue是隊列集合;Map代表的是存儲key-value對的集合,可根據(jù)元素的key來訪問value。
?Java集合和數(shù)組的區(qū)別:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
Guava集合框架介紹
?
Guava 是一款 Google 開源工具類,包含許多 Google 內(nèi)部?Java?項目依賴的核心類。Guava 擴展 Java 基礎(chǔ)類工程,比如集合,并發(fā)等,也增加一些其他強大功能,比如緩存,限流等功能。另外 Guava 推出一些類,如?Optional,甚至被 Java 開發(fā)者學(xué)習(xí),后續(xù)增加到 JDK 中。Guava 創(chuàng)造很多 JDK 沒有,但是我們?nèi)粘s明顯有用的新集合類型。這些新類型使用 JDK 集合接口規(guī)范,所以使用方法與 JDK 集合框架差不多,并沒有增加很多使用難度。
?
| 集合接口 | 屬于JDK還是Guava | 對應(yīng)的Guava工具類 |
| Collection | JDK | Collections2:不要和java.util.Collections混淆 |
| List | JDK | Lists |
| Set | JDK | Sets |
| SortedSet | JDK | Sets |
| Map | JDK | Maps |
| SortedMap | JDK | Maps |
| Queue | JDK | Queues |
| Multiset | Guava | Multisets |
| Multimap | Guava | Multimaps |
| BiMap | Guava | Maps |
| Table | Guava | Tables |
二:接口及類詳解【JAVA】
Iterable<T> 接口
常用方法:
| Iterator<T> iterator(); | 返回每個元素的迭代器 | ? | ? |
| default void forEach(Consumer<? super T> action) | 循環(huán)操作每個元素 | ? | |
| default Spliterator<T> spliterator() | 創(chuàng)建一個spliterator | ? | ? |
重點方法介紹及代碼示例
| /*** iteable測試類*/ public class TestItearble {// 返回迭代器,hasNext方法判斷是否有下一個元素 next方法取下一個元素public static void testIterator(List<Integer> list) {Iterator<Integer> iterator = list.iterator();while (iterator.hasNext()) {System.out.println(iterator.next());}}// foreach 內(nèi)部用的還是iteratorpublic static void testForeach(Set<String> set) {set.forEach(System.out::println);}public static void main(String[] args) {List<Integer> list = new ArrayList<>();list.add(1);list.add(3);list.add(4);testIterator(list);Set<String> set = new HashSet<>();set.add("one");set.add("three");set.add("four");set.add("one");testForeach(set);} |
spliterator是java1.8新提出的能夠進行并行遍歷的迭代器.首先, iterator是專門用于迭代集合元素的方法,在List類中就有iterator()方法.集合通過調(diào)用Iterator方法,可以對該集合進行循環(huán),效果就相當(dāng)于是使用了for循環(huán),但是iterator的好處就是不論是List,還是Map,都可以通過iterator進行遍歷.但是,通過iterator和for循環(huán)一樣,都是單線程的操作,Spliterator也用于遍歷數(shù)據(jù)源中的元素,但它是為了并行執(zhí)行而設(shè)計的。Java 8已經(jīng)為集合框架中包含的所有數(shù)據(jù)結(jié)構(gòu)提供了一個默認的Spliterator實現(xiàn)。集合實現(xiàn)了Spliterator接口,接口提供了一個spliterator方法。詳解可見:https://blog.csdn.net/sl1992/article/details/100149187?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.channel_param
| public class TestSplitror {private static class Thread<T> extends java.lang.Thread {private final Spliterator<T> list;private Thread(Spliterator<T> list) {this.list = list;}@Overridepublic void run() {list.forEachRemaining(System.out::println);}}public static void main(String[] args) {List<String> list = new ArrayList<>();for (int i = 0; i < 2000; i++) {list.add("split" + i);}// 拆分迭代器Spliterator<String> spliterator = list.spliterator();Spliterator<String> stringSpliterator = spliterator.trySplit();Thread<String> stringthread = new Thread<>(spliterator);Thread<String> stringthread1 = new Thread<>(stringSpliterator);stringthread.start();stringthread1.start();while (true) {if (stringthread.isAlive() || stringthread1.isAlive()) {try {Thread.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}} else {break;}}System.out.println("main end");}} |
Collection<E> 接口
常用方法
| int size(); | 返回集合的元素數(shù)量 | 如果元素數(shù)量超過Integer.MAX_VALUE返回Integer.MAX_VALUE. | ? |
| boolean isEmpty(); | 返回集合是否沒有元素 | ? | ? |
| boolean contains(Object o); | 如果集合有該元素返回true | ClassCastException 元素類型不相容 | ? |
| Object[] toArray(); | 返回一個裝有改集合元素的數(shù)組 | 排序 | <T> T[] toArray(T[] a)該方法可以對返回的數(shù)組類型進行精確控制。而非像toArray方法一樣返回Object[]。 |
| boolean add(E e); | 添加一個元素,添加成功返回true | ? | |
| boolean remove(Object o); | 移除一個元素,移除成功返回true | ? | |
| boolean containsAll(Collection<?> c); | 是否存在當(dāng)前集合中的所有元素 | ? | |
| boolean addAll(Collection<? extends E> c); | 添加當(dāng)前集合中的所有元素 | ? | |
| boolean removeAll(Collection<?> c); | 移除當(dāng)前集合中的所有元素 | ? | |
| default boolean removeIf(Predicate<? super E> filter) | 根據(jù)給定的公式移除元素 | ? | |
| default Stream<E> stream() | 返回streeam流 | ? | ? |
| default Stream<E> parallelStream() | 返回并行stream流 | ? | ? |
重點方法介紹及代碼示例
add() 向集合中添加一個元素。集合更改則添加成功返回true,如果該集合不允許重復(fù)并且已經(jīng)包含指定的元素。返回false。部分子類的add方法可能會限制添加到集合中的元素類型,或者不會將NULL添加到集合中。
| public class TestCollectionApi {public static void main(String[] args) {Collection set = new HashSet<>();boolean one = set.add("one");boolean one1 = set.add("one");System.out.println("set第一次添加add" + one);System.out.println("set第二次添加add" + one1);Collection list = new ArrayList<>();boolean one2 = list.add("list");boolean one3 = list.add("list");System.out.println("list第一次添加add" + one2);System.out.println("list第二次添加add" + one3);}} 返回結(jié)果為:true false true true |
List<E>接口
一個排序的集合(也被稱作序列),接口使用者可以精確控制每個元素在集合中的插入位置,用戶可以根據(jù)integer類型的索引訪問和搜索集合中的元素。不像set,集合通常允許重復(fù)的元素,如果他們允許null元素存在的話,也可以存在重復(fù)的null。
?
| default void replaceAll(UnaryOperator<E> operator) | 將此列表中的每個元素替換為對該元素應(yīng)用運算符的結(jié)果。 | ? | ? |
| default void sort(Comparator<? super E> c) | 根據(jù)明確的比較器進行排序 | ? | ? |
| E get(int index); | 返回該位置的元素 | ? | ? |
| E set(int index, E element); | 用明確的元素替換明確位置的元素。 | ? | ? |
| void add(int index, E element); | 插入元素到固定的位置,原該位置元素及后面的元素索引加一。 | ? | ? |
| E remove(int index); | 移除具體位置的元素,后面的元素索引減一 | ? | ? |
| int indexOf(Object o); | 返回該元素的索引位置或者返回-1(如果不存在) | ? | ? |
| ListIterator<E> listIterator(); | 返回一個list迭代器 | int lastIndexOf(Object o);返回最后一個該元素的索引位置或者-1(不存在) | |
| List<E> subList(int fromIndex, int toIndex); | 返回一個前閉后開的視圖。 | ? | ? |
重點方法介紹及代碼示例
| public class TestListApi {public static void main(String[] args) {List<String> list = new LinkedList<>();list.add("a");list.add("b");list.add("c");list.set(1, "d");System.out.println(list);list.add(1, "k");System.out.println(list);list.replaceAll(x -> {if (x.equals("a")) {return "tt";}return x;});System.out.println(list);list.sort(String::compareTo);System.out.println(list);List<String> list1 = list.subList(0, 2);list1.set(0, "kkkk");System.out.println(list);} } 返回結(jié)果為: [a, d, c] [a, k, d, c] [tt, k, d, c] [c, d, k, tt] [kkkk, d, k, tt] |
Set<E>接口
?
元素?zé)o放入順序,元素不可重復(fù)(注意:元素雖然無放入順序,但是元素在set中的位置是有該元素的HashCode決定的,其位置其實是固定的) Set接口有兩個實現(xiàn)類:HashSet(底層由HashMap實現(xiàn)),LinkedHashSet? SortedSet接口有一個實現(xiàn)類:TreeSet(底層由平衡二叉樹實現(xiàn)) Set : 存入Set的每個元素都必須是唯一的,因為Set不保存重復(fù)元素。加入Set的元素必須定義equals()方法以確保對象的唯一性。Set與Collection有完全一樣的接口。Set接口不保證維護元素的次序。 HashSet : 為快速查找設(shè)計的Set。存入HashSet的對象必須定義hashCode()。 TreeSet : 保存次序的Set, 底層為樹結(jié)構(gòu)。使用它可以從Set中提取有序的序列。 LinkedHashSet : 具有HashSet的查詢速度,且內(nèi)部使用鏈表維護元素的順序(插入的次序)。于是在使用迭代器遍歷Set時,結(jié)果會按元素插入的次序顯示。| boolean contains(Object o); | 如果集合有該元素返回true | ClassCastException 元素類型不相容 | ? |
| Object[] toArray(); | 返回一個裝有改集合元素的數(shù)組 | 排序 | <T> T[] toArray(T[] a)該方法可以對返回的數(shù)組類型進行精確控制。而非像toArray方法一樣返回Object[]。 |
| boolean add(E e); | 添加一個元素,添加成功返回true | ? | |
| boolean remove(Object o); | 移除一個元素,移除成功返回true | ? | |
| boolean containsAll(Collection<?> c); | 是否存在當(dāng)前集合中的所有元素 | ? | |
| boolean retainAll(Collection<?> c); | 取交集 | ? | ? |
| boolean addAll(Collection<? extends E> c); | 添加當(dāng)前集合中的所有元素 | ? | |
| boolean removeAll(Collection<?> c); | 移除當(dāng)前集合中的所有元素 | ? | |
| default boolean removeIf(Predicate<? super E> filter) | 根據(jù)給定的公式移除元素 | ? |
TreeSet
TreeSet實現(xiàn)了SortedSet接口,它是一個有序的集合類,TreeSet的底層是通過TreeMap實現(xiàn)的。TreeSet并不是根據(jù)插入的順序來排序,而是根據(jù)實際的值的大小來排序。TreeSet也支持兩種排序方式:
1:自然排序。2:自定義排序
如何保證排序和唯一性?
| public V put(K key, V value) { // 先以 t 保存鏈表的 root 節(jié)點Entry<K,V> t = root; // 如果 t==null,表明是一個空鏈表,即該 TreeMap 里沒有任何 Entry if (t == null) { // 將新的 key-value 創(chuàng)建一個 Entry,并將該 Entry 作為 root root = new Entry<K,V>(key, value, null); // 設(shè)置該 Map 集合的 size 為 1,代表包含一個 Entry size = 1; // 記錄修改次數(shù)為 1 modCount++; return null; } int cmp; Entry<K,V> parent; Comparator<? super K> cpr = comparator; // 如果比較器 cpr 不為 null,即表明采用定制排序if (cpr != null) { do { // 使用 parent 上次循環(huán)后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 進行比較cmp = cpr.compare(key, t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左邊節(jié)點if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右邊節(jié)點else if (cmp > 0) t = t.right; // 如果兩個 key 相等,新的 value 覆蓋原有的 value,// 并返回原有的 value else return t.setValue(value); } while (t != null); } else { if (key == null) throw new NullPointerException(); Comparable<? super K> k = (Comparable<? super K>) key; do { // 使用 parent 上次循環(huán)后的 t 所引用的 Entry parent = t; // 拿新插入 key 和 t 的 key 進行比較cmp = k.compareTo(t.key); // 如果新插入的 key 小于 t 的 key,t 等于 t 的左邊節(jié)點if (cmp < 0) t = t.left; // 如果新插入的 key 大于 t 的 key,t 等于 t 的右邊節(jié)點else if (cmp > 0) t = t.right; // 如果兩個 key 相等,新的 value 覆蓋原有的 value,// 并返回原有的 value else return t.setValue(value); } while (t != null); } // 將新插入的節(jié)點作為 parent 節(jié)點的子節(jié)點Entry<K,V> e = new Entry<K,V>(key, value, parent); // 如果新插入 key 小于 parent 的 key,則 e 作為 parent 的左子節(jié)點if (cmp < 0) parent.left = e; // 如果新插入 key 小于 parent 的 key,則 e 作為 parent 的右子節(jié)點else parent.right = e; // 修復(fù)紅黑樹fixAfterInsertion(e); // ①size++; modCount++; return null; } // 每當(dāng)程序希望添加新節(jié)點時:系統(tǒng)總是從樹的根節(jié)點開始比較 —— 即將根節(jié)點當(dāng)成當(dāng)前節(jié)點,如果新增節(jié)點大于當(dāng)前節(jié)點、并且當(dāng)前節(jié)點的右子節(jié)點存在,則以右子節(jié)點作為當(dāng)前節(jié)點;如果新增節(jié)點小于當(dāng)前節(jié)點、并且當(dāng)前節(jié)點的左子節(jié)點存在,則以左子節(jié)點作為當(dāng)前節(jié)點;如果新增節(jié)點等于當(dāng)前節(jié)點,則用新增節(jié)點覆蓋當(dāng)前節(jié)點,并結(jié)束循環(huán) —— 直到找到某個節(jié)點的左、右子節(jié)點不存在,將新節(jié)點添加該節(jié)點的子節(jié)點 —— 如果新節(jié)點比該節(jié)點大,則添加為右子節(jié)點;如果新節(jié)點比該節(jié)點小,則添加為左子節(jié)點。 |
TreeSet的底層實現(xiàn)
簡單來說 treeset就是沒有重復(fù)元素的treemap
| public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, java.io.Serializable { // 使用 NavigableMap 的 key 來保存 Set 集合的元素private transient NavigableMap<E,Object> m; // 使用一個 PRESENT 作為 Map 集合的所有 value。private static final Object PRESENT = new Object(); // 包訪問權(quán)限的構(gòu)造器,以指定的 NavigableMap 對象創(chuàng)建 Set 集合TreeSet(NavigableMap<E,Object> m) { this.m = m; } public TreeSet() // ①{ // 以自然排序方式創(chuàng)建一個新的 TreeMap,// 根據(jù)該 TreeSet 創(chuàng)建一個 TreeSet,// 使用該 TreeMap 的 key 來保存 Set 集合的元素this(new TreeMap<E,Object>()); } public TreeSet(Comparator<? super E> comparator) // ②{ // 以定制排序方式創(chuàng)建一個新的 TreeMap,// 根據(jù)該 TreeSet 創(chuàng)建一個 TreeSet,// 使用該 TreeMap 的 key 來保存 Set 集合的元素this(new TreeMap<E,Object>(comparator)); } public TreeSet(Collection<? extends E> c) { // 調(diào)用①號構(gòu)造器創(chuàng)建一個 TreeSet,底層以 TreeMap 保存集合元素this(); // 向 TreeSet 中添加 Collection 集合 c 里的所有元素addAll(c); } public TreeSet(SortedSet<E> s) { // 調(diào)用②號構(gòu)造器創(chuàng)建一個 TreeSet,底層以 TreeMap 保存集合元素this(s.comparator()); // 向 TreeSet 中添加 SortedSet 集合 s 里的所有元素addAll(s); } } //從上面代碼可以看出,TreeSet 的 ① 號、② 號構(gòu)造器的都是新建一個 TreeMap 作為實際存儲 Set 元素的容器,而另外 2 個構(gòu)造器則分別依賴于 ① 號和 ② 號構(gòu)造器,由此可見,TreeSet 底層實際使用的存儲容器就是 TreeMap。 |
注意:如果想實現(xiàn)自然排序,那么需保證排序的類實現(xiàn)了comparable接口,否則會拋出java.lang.ClassCastException
ArrayList
官方解釋:List接口的動態(tài)數(shù)組實現(xiàn),允許所有類型,包括null,除了實現(xiàn)List接口之外,此類提供方法來操作內(nèi)部用于存儲列表。這個類大致相當(dāng)于Vector,但它不是同步的。
ArrayList初始化
?
| public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {private static final long serialVersionUID = 8683452581122892189L;/*** 默認的容量*/private static final int DEFAULT_CAPACITY = 10;/*** 空數(shù)組,當(dāng)調(diào)用無參數(shù)構(gòu)造函數(shù)的時候默認給個空數(shù)組*/private static final Object[] EMPTY_ELEMENTDATA = {};/*** * 空數(shù)組,當(dāng)調(diào)用無參數(shù)構(gòu)造函數(shù)的時候默認給個空數(shù)組,我們將其與空的元素數(shù)據(jù)區(qū)分開來,以了解添加第一個元素時要膨脹多少。*/private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};/*** 存儲ArrayList中元素的數(shù)組容器,ArrayList的容量就是數(shù)組的長度,任何一個空的數(shù)組都會被拓展到DEFAULT_CAPACITY個容量,當(dāng)?shù)谝粋€元素添加時。*/transient Object[] elementData; // non-private to simplify nested class access/*** 包含的元素的數(shù)量*/private int size;/*** 構(gòu)造方法傳入默認的capacity 設(shè)置默認數(shù)組大小*/public ArrayList(int initialCapacity) {if (initialCapacity > 0) {this.elementData = new Object[initialCapacity];} else if (initialCapacity == 0) {this.elementData = EMPTY_ELEMENTDATA;} else {throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);}}/*** 無參數(shù)構(gòu)造方法默認為空數(shù)組*/public ArrayList() {this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;} // get方法 public E get(int index) {rangeCheck(index);return elementData(index); } E elementData(int index) {return (E) elementData[index]; }} |
ArrayList動態(tài)擴容
| public class ArrayList<E> extends AbstractList<E>implements List<E>, RandomAccess, Cloneable, java.io.Serializable {/*** 添加一個元素到列表的尾部*/ public boolean add(E e) {ensureCapacityInternal(size + 1); // Increments modCount!!elementData[size++] = e;return true; }private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)); } private void ensureExplicitCapacity(int minCapacity) {modCount++;// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity); } private static int calculateCapacity(Object[] elementData, int minCapacity) {if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {return Math.max(DEFAULT_CAPACITY, minCapacity);}return minCapacity; } /*** 增加容量確保他可以保存minCapacity的元素*/ private void grow(int minCapacity) {// 首先容量翻1.5倍int oldCapacity = elementData.length;int newCapacity = oldCapacity + (oldCapacity >> 1);// 翻倍之后還不滿足,容量賦值為所需最小容量if (newCapacity - minCapacity < 0)newCapacity = minCapacity;// 最大值為Integer.MAX_VALUEif (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);// 將原來數(shù)組的值copy新數(shù)組中去, ArrayList的引用指向新數(shù)組elementData = Arrays.copyOf(elementData, newCapacity); }private static int hugeCapacity(int minCapacity) {if (minCapacity < 0) // overflowthrow new OutOfMemoryError();return (minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE : MAX_ARRAY_SIZE; }/*** 移除制定位置的元素,返回值為被移除的元素*/ public E remove(int index) {rangeCheck(index);modCount++;E oldValue = elementData(index);int numMoved = size - index - 1;if (numMoved > 0)// 移除元素右邊的元素左移System.arraycopy(elementData, index+1, elementData, index,numMoved);// 最后一位置空elementData[--size] = null; // clear to let GC do its work ???return oldValue; } } |
Array.copyOf與System.ArrayCopyOf
?
| public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {@SuppressWarnings("unchecked")T[] copy = ((Object)newType == (Object)Object[].class)? (T[]) new Object[newLength]: (T[]) Array.newInstance(newType.getComponentType(), newLength);System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));return copy; } |
由此可見Array.copyOf與System.ArrayCopyOf均為淺拷貝。
HashMap
?
HashMap?是一個關(guān)聯(lián)數(shù)組、哈希表,它是線程不安全的,允許key為null,value為null。遍歷時無序。其底層數(shù)據(jù)結(jié)構(gòu)是數(shù)組稱之為哈希桶,每個桶里面放的是鏈表,鏈表中的每個節(jié)點,就是哈希表中的每個元素。
在JDK8中,當(dāng)鏈表長度達到8,會轉(zhuǎn)化成紅黑樹,以提升它的查詢、插入效率.
?
因其底層哈希桶的數(shù)據(jù)結(jié)構(gòu)是數(shù)組,所以也會涉及到擴容的問題。
當(dāng)HashMap的容量達到threshold域值時,就會觸發(fā)擴容。擴容前后,哈希桶的長度一定會是2的次方。這樣在根據(jù)key的hash值尋找對應(yīng)的哈希桶時,可以用位運算替代取余操作,更加高效。而key的hash值,并不僅僅只是key對象的hashCode()方法的返回值,還會經(jīng)過擾動函數(shù)的擾動,以使hash值更加均衡。
但就算原本的hashCode()取得很好,每個key的hashCode()不同,但是由于HashMap的哈希桶的長度遠比hash取值范圍小,默認是16,所以當(dāng)對hash值以桶的長度取余,以找到存放該key的桶的下標(biāo)時,由于取余是通過與操作完成的,會忽略hash值的高位。因此只有hashCode()的低位參加運算,發(fā)生不同的hash值,但是得到的index相同的情況的幾率會大大增加,這種情況稱之為hash碰撞。?即,碰撞率會增大。擾動函數(shù)就是為了解決hash碰撞的。它會綜合hash值高位和低位的特征,并存放在低位,因此在與運算時,相當(dāng)于高低位一起參與了運算,以減少hash碰撞的概率。(在JDK8之前,擾動函數(shù)會擾動四次,JDK8簡化了這個操作)
?
擴容操作時,會new一個新的Node數(shù)組作為哈希桶,然后將原哈希表中的所有數(shù)據(jù)(Node節(jié)點)移動到新的哈希桶中,相當(dāng)于對原哈希表中所有的數(shù)據(jù)重新做了一個put操作。所以性能消耗很大,可想而知,在哈希表的容量越大時,性能消耗越明顯。
?
數(shù)據(jù)域
| /*** 默認初始容量16*/ static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16/*** 最大容量必須小于 1 << 3*/ static final int MAXIMUM_CAPACITY = 1 << 30;/*** load factor */ static final float DEFAULT_LOAD_FACTOR = 0.75f;/*** 當(dāng)add一個元素到某個位桶,其鏈表長度達到8時將鏈表轉(zhuǎn)換為紅黑樹*/ static final int TREEIFY_THRESHOLD = 8;/****/ static final int UNTREEIFY_THRESHOLD = 6; /*** 存儲元素的數(shù)組*/ transient Node<K,V>[] table;/*** */ transient Set<Map.Entry<K,V>> entrySet;/*** 包含的鍵值對的個數(shù)*/ transient int size;/*** 結(jié)構(gòu)性修改的次數(shù),fast—fail機制*/ transient int modCount; /*** */ int threshold;/*** The load factor for the hash table.*/ final float loadFactor;static class Node<K,V> implements Map.Entry<K,V> {//哈希值 final int hash;final K key;V value;//鏈表后置節(jié)點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;}public final K getKey() { return key; }public final V getValue() { return value; }public final String toString() { return key + "=" + value; }//每一個節(jié)點的hash值,是將key的hashCode 和 value的hashCode 亦或得到的。public final int hashCode() {return Objects.hashCode(key) ^ Objects.hashCode(value);}//設(shè)置新的value 同時返回舊valuepublic final V setValue(V newValue) {V oldValue = value;value = newValue;return oldValue;}public final boolean equals(Object o) {if (o == this)return true;if (o instanceof Map.Entry) {Map.Entry<?,?> e = (Map.Entry<?,?>)o;if (Objects.equals(key, e.getKey()) &&Objects.equals(value, e.getValue()))return true;}return false;}} |
構(gòu)造函數(shù)
| public HashMap() {//默認構(gòu)造函數(shù),賦值加載因子為默認的0.75fthis.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted}public HashMap(int initialCapacity) {//指定初始化容量的構(gòu)造函數(shù)this(initialCapacity, DEFAULT_LOAD_FACTOR);}//同時指定初始化容量 以及 加載因子, 用的很少,一般不會修改loadFactorpublic HashMap(int initialCapacity, float loadFactor) {//邊界處理if (initialCapacity < 0)throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);//初始容量最大不能超過2的30次方if (initialCapacity > MAXIMUM_CAPACITY)initialCapacity = MAXIMUM_CAPACITY;//加載因子不能為負數(shù)if (loadFactor <= 0 || Float.isNaN(loadFactor))throw new IllegalArgumentException("Illegal load factor: " +loadFactor);this.loadFactor = loadFactor;//設(shè)置閾值為 》=初始化容量的 2的n次方的值this.threshold = tableSizeFor(initialCapacity);}//新建一個哈希表,同時將另一個map m 里的所有元素加入表中public HashMap(Map<? extends K, ? extends V> m) {this.loadFactor = DEFAULT_LOAD_FACTOR;putMapEntries(m, false);}//根據(jù)期望容量cap,返回2的n次方形式的 哈希桶的實際容量 length。 返回值一般會>=cap static final int tableSizeFor(int cap) {//經(jīng)過下面的 或 和位移 運算, n最終各位都是1。int n = cap - 1;n |= n >>> 1;n |= n >>> 2;n |= n >>> 4;n |= n >>> 8;n |= n >>> 16;//判斷n是否越界,返回 2的n次方作為 table(哈希桶)的閾值return (n < 0) ? 1 : (n >= MAXIMUM_CAPACITY) ? MAXIMUM_CAPACITY : n + 1;} |
擴容機制
| public V put(K key, V value) {return putVal(hash(key), key, value, false, true); } final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//tab存放 當(dāng)前的哈希桶, p用作臨時鏈表節(jié)點 Node<K,V>[] tab; Node<K,V> p; int n, i;//如果當(dāng)前哈希表是空的,代表是初始化if ((tab = table) == null || (n = tab.length) == 0)//那么直接去擴容哈希表,并且將擴容后的哈希桶長度賦值給nn = (tab = resize()).length;//如果當(dāng)前index的節(jié)點是空的,表示沒有發(fā)生哈希碰撞。 直接構(gòu)建一個新節(jié)點Node,掛載在index處即可。// index 是利用 哈希值 & 哈希桶的長度-1,替代模運算if ((p = tab[i = (n - 1) & hash]) == null)tab[i] = newNode(hash, key, value, null);else {//否則 發(fā)生了哈希沖突。//eNode<K,V> e; K k;//如果哈希值相等,key也相等,則是覆蓋value操作if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))e = p;//將當(dāng)前節(jié)點引用賦值給eelse if (p instanceof TreeNode)//紅黑樹暫且不談e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else {//不是覆蓋操作,則插入一個普通鏈表節(jié)點//遍歷鏈表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//遍歷到尾部,追加新節(jié)點到尾部p.next = newNode(hash, key, value, null);//如果追加節(jié)點后,鏈表數(shù)量》=8,則轉(zhuǎn)化為紅黑樹if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1sttreeifyBin(tab, hash);break;}//如果找到了要覆蓋的節(jié)點if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))break;p = e;}}//如果e不是null,說明有需要覆蓋的節(jié)點,if (e != null) { // existing mapping for key//則覆蓋節(jié)點值,并返回原oldValueV oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;//這是一個空實現(xiàn)的函數(shù),用作LinkedHashMap重寫使用。afterNodeAccess(e);return oldValue;}}//如果執(zhí)行到了這里,說明插入了一個新的節(jié)點,所以會修改modCount,以及返回null。//修改modCount++modCount;//更新size,并判斷是否需要擴容。if (++size > threshold)resize();//這是一個空實現(xiàn)的函數(shù),用作LinkedHashMap重寫使用。afterNodeInsertion(evict);return null;}final Node<K,V>[] resize() {//oldTab 為當(dāng)前表的哈希桶Node<K,V>[] oldTab = table;//當(dāng)前哈希桶的容量 lengthint oldCap = (oldTab == null) ? 0 : oldTab.length;//當(dāng)前的閾值int oldThr = threshold;//初始化新的容量和閾值為0int newCap, newThr = 0;//如果當(dāng)前容量大于0if (oldCap > 0) {//如果當(dāng)前容量已經(jīng)到達上限if (oldCap >= MAXIMUM_CAPACITY) {//則設(shè)置閾值是2的31次方-1threshold = Integer.MAX_VALUE;//同時返回當(dāng)前的哈希桶,不再擴容return oldTab;}//否則新的容量為舊的容量的兩倍。 else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&oldCap >= DEFAULT_INITIAL_CAPACITY)//如果舊的容量大于等于默認初始容量16//那么新的閾值也等于舊的閾值的兩倍newThr = oldThr << 1; // double threshold}//如果當(dāng)前表是空的,但是有閾值。代表是初始化時指定了容量、閾值的情況else if (oldThr > 0) // initial capacity was placed in thresholdnewCap = oldThr;//那么新表的容量就等于舊的閾值else {}//如果當(dāng)前表是空的,而且也沒有閾值。代表是初始化時沒有任何容量/閾值參數(shù)的情況 newCap = DEFAULT_INITIAL_CAPACITY;//此時新表的容量為默認的容量 16newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);//新的閾值為默認容量16 * 默認加載因子0.75f = 12}if (newThr == 0) {//如果新的閾值是0,對應(yīng)的是 當(dāng)前表是空的,但是有閾值的情況float ft = (float)newCap * loadFactor;//根據(jù)新表容量 和 加載因子 求出新的閾值//進行越界修復(fù)newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?(int)ft : Integer.MAX_VALUE);}//更新閾值 threshold = newThr;@SuppressWarnings({"rawtypes","unchecked"})//根據(jù)新的容量 構(gòu)建新的哈希桶Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];//更新哈希桶引用table = newTab;//如果以前的哈希桶中有元素//下面開始將當(dāng)前哈希桶中的所有節(jié)點轉(zhuǎn)移到新的哈希桶中if (oldTab != null) {//遍歷老的哈希桶for (int j = 0; j < oldCap; ++j) {//取出當(dāng)前的節(jié)點 eNode<K,V> e;//如果當(dāng)前桶中有元素,則將鏈表賦值給eif ((e = oldTab[j]) != null) {//將原哈希桶置空以便GColdTab[j] = null;//如果當(dāng)前鏈表中就一個元素,(沒有發(fā)生哈希碰撞)if (e.next == null)//直接將這個元素放置在新的哈希桶里。//注意這里取下標(biāo) 是用 哈希值 與 桶的長度-1 。 由于桶的長度是2的n次方,這么做其實是等于 一個模運算。但是效率更高newTab[e.hash & (newCap - 1)] = e;//如果發(fā)生過哈希碰撞 ,而且是節(jié)點數(shù)超過8個,轉(zhuǎn)化成了紅黑樹else if (e instanceof TreeNode)((TreeNode<K,V>)e).split(this, newTab, j, oldCap);//如果發(fā)生過哈希碰撞,節(jié)點數(shù)小于8個。則要根據(jù)鏈表上每個節(jié)點的哈希值,依次放入新哈希桶對應(yīng)下標(biāo)位置。else { // preserve order//因為擴容是容量翻倍,所以原鏈表上的每個節(jié)點,現(xiàn)在可能存放在原來的下標(biāo),即low位, 或者擴容后的下標(biāo),即high位。 high位= low位+原哈希桶容量//低位鏈表的頭結(jié)點、尾節(jié)點Node<K,V> loHead = null, loTail = null;//高位鏈表的頭節(jié)點、尾節(jié)點Node<K,V> hiHead = null, hiTail = null;Node<K,V> next;//臨時節(jié)點 存放e的下一個節(jié)點do {next = e.next;//這里又是一個利用位運算 代替常規(guī)運算的高效點: 利用哈希值 與 舊的容量,可以得到哈希值去模后,是大于等于oldCap還是小于oldCap,等于0代表小于oldCap,應(yīng)該存放在低位,否則存放在高位if ((e.hash & oldCap) == 0) {//給頭尾節(jié)點指針賦值if (loTail == null)loHead = e;elseloTail.next = e;loTail = e;}//高位也是相同的邏輯else {if (hiTail == null)hiHead = e;elsehiTail.next = e;hiTail = e;}//循環(huán)直到鏈表結(jié)束} while ((e = next) != null);//將低位鏈表存放在原index處,if (loTail != null) {loTail.next = null;newTab[j] = loHead;}//將高位鏈表存放在新index處if (hiTail != null) {hiTail.next = null;newTab[j + oldCap] = hiHead;}}}}}return newTab;} |
https://blog.csdn.net/tuke_tuke/article/details/51588156?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
HashSet
對于HashSet而言,它是基于HashMap實現(xiàn)的,HashSet底層使用HashMap來保存所有元素,因此HashSet 的實現(xiàn)比較簡單.
| private transient HashMap<E,Object> map;// 默認的value值 private static final Object PRESENT = new Object();/*** 無參構(gòu)造方法,默認容量16,loadfactory0.75*/ public HashSet() {map = new HashMap<>(); }/*** 構(gòu)造方法,默認容量為大于參數(shù)容量的2.冪次方,loadfactory0.75*/ public HashSet(Collection<? extends E> c) {map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));addAll(c); }/*** 自定義容量和loadfactory*/ public HashSet(int initialCapacity, float loadFactor) {map = new HashMap<>(initialCapacity, loadFactor); }/*** 自定義容量 loadfactory0.75*/ public HashSet(int initialCapacity) {map = new HashMap<>(initialCapacity); }/*** 自定義容量 loadfactory 和默認value*/ HashSet(int initialCapacity, float loadFactor, boolean dummy) {map = new LinkedHashMap<>(initialCapacity, loadFactor); }/*** */ public boolean contains(Object o) {return map.containsKey(o); }/*** 講元素置為key*/ public boolean add(E e) {return map.put(e, PRESENT)==null; } |
?
BitSet
Bitset是Java中的一種數(shù)據(jù)結(jié)構(gòu)。Bitset中主要存儲的是二進制位,做的也都是位運算,每一位只用來存儲0,1值,主要用于對數(shù)據(jù)的標(biāo)記,用1位來表示一個數(shù)據(jù)是否出現(xiàn)過,0為沒有出現(xiàn)過,1表示出現(xiàn)過。(非線程安全)
使用場景:整數(shù),無重復(fù)。
參考文檔:https://blog.csdn.net/kongmin_123/article/details/82225172?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.channel_param
BitSet的底層實現(xiàn)是使用long數(shù)組作為內(nèi)部存儲結(jié)構(gòu)的,這就決定了BitSet至少為一個long的大小,而且BitSet的大小為long類型大小(64位)的整數(shù)倍。| /** 位集被放到一個long型的數(shù)組中,由64位組成,需要6個地址位。*/ private final static int ADDRESS_BITS_PER_WORD = 6; private final static int BITS_PER_WORD = 1 << ADDRESS_BITS_PER_WORD; private final static int BIT_INDEX_MASK = BITS_PER_WORD - 1;/* Used to shift left or right for a partial word mask */ private static final long WORD_MASK = 0xffffffffffffffffL;/*** @serialField bits long[]** The bits in this BitSet. The ith bit is stored in bits[i/64] at* bit position i % 64 (where bit position 0 refers to the least* significant bit and 63 refers to the most significant bit).*/ private static final ObjectStreamField[] serialPersistentFields = {new ObjectStreamField("bits", long[].class), };/*** 底層存儲結(jié)構(gòu)*/ private long[] words; |
BitSet的構(gòu)造函數(shù)有兩個,如果指定了初始化大小,那么會把他規(guī)整到一個大于或者等于這個數(shù)字的64的整倍數(shù)。比如64位,BitSet的大小是1個long,而65位時,指定了大小是2個long,即128位。做這么一個規(guī)定,主要是為了內(nèi)存對齊,同時避免考慮到特殊情況的處理,簡化程序。
| public BitSet() {initWords(BITS_PER_WORD);sizeIsSticky = false; }public BitSet(int nbits) {// nbits不能為負;大小為0可以if (nbits < 0)throw new NegativeArraySizeException("nbits < 0: " + nbits);initWords(nbits);sizeIsSticky = true; }// 給定索引,返回新的數(shù)組private void initWords(int nbits) {words = new long[wordIndex(nbits-1) + 1]; }private static int wordIndex(int bitIndex) {return bitIndex >> ADDRESS_BITS_PER_WORD; } |
BitSet如何存儲數(shù)據(jù)?怎么快速定位他的存儲位置?
BitSet的默認初始大小是一個long數(shù)組,一個long數(shù)組就是64個bit,每一個bit的值就是二進制的0或者1,bit的值和相應(yīng)位置就代表一個數(shù)在不在BitSet當(dāng)中,0代表該數(shù)值不存在,1代表該數(shù)組值存在。這樣就可以描述數(shù)據(jù)對數(shù)據(jù)進行標(biāo)記了。
?
在這里,0、3、63等存放入了long數(shù)組中。
從上面的BitSet的結(jié)構(gòu)圖我們可以看到,要想定位一個數(shù)據(jù),需要確定兩個值:
(1)這個數(shù)位于哪個數(shù)組,也就是確定words[wordIndex]?的wordIndex是多少。
(2)這個數(shù)位于數(shù)組的哪一部分,也就是確定這個數(shù)的bitIndex是哪一位。
| public void set(int bitIndex) {// 傳入的下標(biāo)是否越界if (bitIndex < 0)throw new IndexOutOfBoundsException("bitIndex < 0: " + bitIndex); // 判斷要存入的bitIndex應(yīng)該在那個數(shù)組中int wordIndex = wordIndex(bitIndex); // 如果現(xiàn)有數(shù)組不能滿足,拓展大小expandTo(wordIndex); // 進行的邏輯運算,對1進行左移,然后與words[wordIndex]做或(or)運算。words[wordIndex] |= (1L << bitIndex); // Restores invariantscheckInvariants(); } |
BitSet的用法
| BitSet bitSet = new BitSet(); bitSet.set(50); // 位數(shù) 64 System.out.println(bitSet.size()); bitSet.set(130); // 位數(shù) 192 System.out.println(bitSet.size()); // 是否存在 boolean b = bitSet.get(130); System.out.println(b); // stream boolean match = bitSet.stream().anyMatch(bit -> Objects.equals(bit, 130)); System.out.println(match); // 清空所有bit位 清空某一位 clear(int bitIndex) bitSet.clear(); // 反轉(zhuǎn) bitSet.flip(30);BitSet bitSet1 = new BitSet(); bitSet1.set(50); bitSet1.set(250); // 并集 bitSet1.or(bitSet); // 交集 bitSet.and(bitSet1); // 補集 bitSet.andNot(bitSet1); |
BitSet的應(yīng)用:
(1)大數(shù)據(jù)量的查找。
(2)大數(shù)據(jù)量的去重。
(3)大數(shù)據(jù)量的統(tǒng)計。
(4)大數(shù)據(jù)量的排序。
(5)求數(shù)據(jù)的并集、交集、補集等。
(6)大數(shù)據(jù)量的判別。
? BitSet常見的應(yīng)用是那些對海量數(shù)據(jù)進行一些統(tǒng)計工作,比如日志分析、用戶數(shù)統(tǒng)計等等。
具體可參考:https://blog.csdn.net/kongmin_123/article/details/82257209
三:接口及類詳解【GUAVA】
https://blog.csdn.net/kuyuyingzi/article/details/30529053?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.channel_param&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5.channel_param
BiMap接口
繼承自:?java.util.Map;bimap(或“雙向映射”)是一種保持其值和鍵的唯一性的映射。此約束使bimap支持“反向視圖”,即另一個bimap,包含與此bimap相同的條目,但具有相反的鍵和值。 目前的實現(xiàn)類:EnumBiMap,?EnumHashBiMap,?HashBiMap,?ImmutableBiMap
?
| public class GuavaTester {public static void main(String args[]){BiMap<Integer, String> empIDNameMap = HashBiMap.create();empIDNameMap.put(new Integer(101), "Mahesh");empIDNameMap.put(new Integer(102), "Sohan");empIDNameMap.put(new Integer(103), "Ramesh");//Emp Id of Employee "Mahesh"System.out.println(empIDNameMap.inverse().get("Mahesh"));} 輸出結(jié)果:101 |
?
Multiset
Collection<E>,?Iterable<E>;支持順序獨立相等的集合,如Set,但可能有重復(fù)的元素。多套有時也被稱為包。多集合中彼此相等的元素被稱為同一個元素的出現(xiàn)。一個元素在multiset中出現(xiàn)的總數(shù)稱為該元素的計數(shù)(“frequency”和“multiplicity”這兩個術(shù)語是等價的,但在這個API中沒有使用)。由于元素的計數(shù)用int表示,因此multiset的值永遠不能超過Integer.MAX_值任何一個元素的出現(xiàn)。
目前的實現(xiàn)類:ConcurrentHashMultiset,?EnumMultiset,?ForwardingMultiset,?ForwardingSortedMultiset,?ForwardingSortedMultiset.StandardDescendingMultiset,?HashMultiset,?ImmutableMultiset,?ImmutableSortedMultiset,?LinkedHashMultiset,?TreeMultiset
?
| public class GuavaTester {public static void main(String args[]){//create a multiset collectionMultiset<String> multiset = HashMultiset.create();multiset.add("a");multiset.add("b");multiset.add("c");multiset.add("d");multiset.add("a");multiset.add("b");multiset.add("c");multiset.add("b");multiset.add("b");multiset.add("b");//print the occurrence of an elementSystem.out.println("Occurrence of 'b' : "+multiset.count("b"));//print the total size of the multisetSystem.out.println("Total Size : "+multiset.size());//get the distinct elements of the multiset as setSet<String> set = multiset.elementSet();//display the elements of the setSystem.out.println("Set [");for (String s : set) { System.out.println(s); }System.out.println("]");//display all the elements of the multiset using iteratorIterator<String> iterator = multiset.iterator();System.out.println("MultiSet [");while(iterator.hasNext()){System.out.println(iterator.next());}System.out.println("]"); //display the distinct elements of the multiset with their occurrence countSystem.out.println("MultiSet [");for (Multiset.Entry<String> entry : multiset.entrySet()){System.out.println("Element: "+entry.getElement() +", Occurrence(s): " + entry.getCount()); }System.out.println("]"); //remove extra occurrences multiset.remove("b",2);//print the occurrence of an elementSystem.out.println("Occurence of 'b' : "+multiset.count("b"));} 輸出結(jié)果: Occurence of 'b' : 5 Total Size : 10 Set [ d b c a ] MultiSet [ d b b b b b c c a a ] MultiSet [ Element: d, Occurence(s): 1 Element: b, Occurence(s): 5 Element: c, Occurence(s): 2 Element: a, Occurence(s): 2 ] Occurence of 'b' : 3 |
Multimaps
?
提供作用于或生成多重映射的靜態(tài)方法。
index
作為Maps.uniqueIndex的兄弟方法,Multimaps.index(Iterable, Function)通常針對的場景是:有一組對象,它們有共同的特定屬性,我們希望按照這個屬性的值查詢對象,但屬性值不一定是獨一無二的。比方說,我們想把字符串按長度分組。
| ImmutableSet digits = ImmutableSet.of("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine");Function<String, Integer> lengthFunction = new Function<String, Integer>() {public Integer apply(String string) {return string.length();} }; ImmutableListMultimap<Integer, String> digitsByLength= Multimaps.index(digits, lengthFunction); /* * digitsByLength maps: * 3 => {"one", "two", "six"} * 4 => {"zero", "four", "five", "nine"} * 5 => {"three", "seven", "eight"} */ |
invertFrom
鑒于Multimap可以把多個鍵映射到同一個值(譯者注:實際上這是任何map都有的特性),也可以把一個鍵映射到多個值,反轉(zhuǎn)Multimap也會很有用。Guava 提供了invertFrom(Multimap toInvert,
Multimap dest)做這個操作,并且你可以自由選擇反轉(zhuǎn)后的Multimap實現(xiàn)。
注:如果你使用的是ImmutableMultimap,考慮改用ImmutableMultimap.inverse()做反轉(zhuǎn)。
| ArrayListMultimap<String, Integer> multimap = ArrayListMultimap.create(); multimap.putAll("b", Ints.asList(2, 4, 6)); multimap.putAll("a", Ints.asList(4, 2, 1)); multimap.putAll("c", Ints.asList(2, 5, 3)); TreeMultimap<Integer, String> inverse = Multimaps.invertFrom(multimap, TreeMultimap<String, Integer>.create()); //注意我們選擇的實現(xiàn),因為選了TreeMultimap,得到的反轉(zhuǎn)結(jié)果是有序的 /* * inverse maps: * 1 => {"a"} * 2 => {"a", "b", "c"} * 3 => {"c"} * 4 => {"a", "b"} * 5 => {"c"} * 6 => {"b"} */ |
forMap
想在Map對象上使用Multimap的方法嗎?forMap(Map)把Map包裝成SetMultimap。這個方法特別有用,例如,與Multimaps.invertFrom結(jié)合使用,可以把多對一的Map反轉(zhuǎn)為一對多的Multimap。
| Map<String, Integer> map = ImmutableMap.of("a", 1, "b", 1, "c", 2); SetMultimap<String, Integer> multimap = Multimaps.forMap(map); // multimap:["a" => {1}, "b" => {1}, "c" => {2}] Multimap<Integer, String> inverse = Multimaps.invertFrom(multimap, HashMultimap<Integer, String>.create()); // inverse:[1 => {"a","b"}, 2 => {"c"}] |
Table
Table代表一個特殊的映射,其中兩個鍵可以在組合的方式被指定為單個值。它類似于創(chuàng)建映射的映射。
?
| public class GuavaTester {public static void main(String args[]){//Table<R,C,V> == Map<R,Map<C,V>>/** Company: IBM, Microsoft, TCS* IBM -> {101:Mahesh, 102:Ramesh, 103:Suresh}* Microsoft -> {101:Sohan, 102:Mohan, 103:Rohan } * TCS -> {101:Ram, 102: Shyam, 103: Sunil } * * *///create a tableTable<String, String, String> employeeTable = HashBasedTable.create();//initialize the table with employee detailsemployeeTable.put("IBM", "101","Mahesh");employeeTable.put("IBM", "102","Ramesh");employeeTable.put("IBM", "103","Suresh");employeeTable.put("Microsoft", "111","Sohan");employeeTable.put("Microsoft", "112","Mohan");employeeTable.put("Microsoft", "113","Rohan");employeeTable.put("TCS", "121","Ram");employeeTable.put("TCS", "122","Shyam");employeeTable.put("TCS", "123","Sunil");//get Map corresponding to IBMMap<String,String> ibmEmployees = employeeTable.row("IBM");System.out.println("List of IBM Employees");for(Map.Entry<String, String> entry : ibmEmployees.entrySet()){System.out.println("Emp Id: " + entry.getKey() + ", Name: " + entry.getValue());}//get all the unique keys of the tableSet<String> employers = employeeTable.rowKeySet();System.out.print("Employers: ");for(String employer: employers){System.out.print(employer + " ");}System.out.println();//get a Map corresponding to 102Map<String,String> EmployerMap = employeeTable.column("102");for(Map.Entry<String, String> entry : EmployerMap.entrySet()){System.out.println("Employer: " + entry.getKey() + ", Name: " + entry.getValue());} } }輸出: List of IBM Employees Emp Id: 102, Name: Ramesh Emp Id: 101, Name: Mahesh Emp Id: 103, Name: Suresh Employers: IBM TCS Microsoft Employer: IBM, Name: Ramesh |
四:常用API及原理解析
常用API
Map常用API
| HashMap<String, String> map6 = Maps.newHashMap(); // 如果不存在則存入 map6.putIfAbsent("one", "1"); map6.putIfAbsent("one", "2"); // 存在覆蓋,不存在存入 map6.put("one","3"); map6.put("two", "2"); // 根據(jù)給出的function計算value map6.compute("three", (x, y) -> x + y + "compute"); map6.computeIfPresent("two",(x,y) -> x + y + "compute1"); map6.computeIfAbsent() // 根據(jù)key和funtion對value進行merge map6.merge("one", "8", (x, y) -> x + y); |
集合常用API
| List<YearMonthBo> yearMonthBoList = Lists.newArrayList(); // 根據(jù)給定的方式移除元素,有元素移除返回true,可以為空,不可以為null,注意源碼移除方式,用其他循環(huán)代替是否可行? boolean isRemove = yearMonthBoList.removeIf(x -> Objects.nonNull(x)); yearMonthBoList.add(new YearMonthBo(2020, 11)); yearMonthBoList.add(new YearMonthBo(2020, 12)); yearMonthBoList.add(new YearMonthBo(2020, 11)); yearMonthBoList.add(new YearMonthBo(2019, 11)); // 根據(jù)給定排序方式排序,可以為空,不可以為null yearMonthBoList.sort(Comparator.comparing(YearMonthBo::getMonth)); // 添加集合元素,可以為空,不可以為null yearMonthBoList.addAll(Lists.newArrayList(new YearMonthBo(2020,6))); yearMonthBoList.add(new YearMonthBo(2020,4)); // get set remove foreach size indexOf等方法不再贅述 |
Stack常用API
| // stack繼承vector,實現(xiàn)了List接口,有List接口所有功能 Stack<String> stack = new Stack<>(); // 放入一個元素 stack.push("a"); // 彈出元素 String pop = stack.pop(); // 取棧頂元素 String peek = stack.peek(); |
Queue常用API
| // 隊列先進先出 Queue<String> queue = new ArrayBlockingQueue<String>(10); // 添加元素 queue.offer("a"); queue.offer("b"); // 隊列頭部元素 String peek = queue.peek(); // 取出隊列頭部元素 String poll = queue.poll(); |
常見集合處理方式
guava集合交集,差集,并集
| Set<Integer> sets = Sets.newHashSet(1, 2, 3, 4, 5, 6);Set<Integer> sets2 = Sets.newHashSet(3, 4, 5, 6, 7, 8, 9);// 交集System.out.println("交集為:");SetView<Integer> intersection = Sets.intersection(sets, sets2);for (Integer temp : intersection) {System.out.println(temp);}// 差集System.out.println("差集為:");SetView<Integer> diff = Sets.difference(sets, sets2);for (Integer temp : diff) {System.out.println(temp);}// 并集System.out.println("并集為:");SetView<Integer> union = Sets.union(sets, sets2);for (Integer temp : union) {System.out.println(temp);} |
java集合交集,差集,并集
| public static void main(String[] args) {List<String> list1 = new ArrayList<String>();list1.add("A");list1.add("B");list1.add("C");List<String> list2 = new ArrayList<String>();list2.add("C");list2.add("B");list2.add("D");// 并集list1.addAll(list2);// 去重復(fù)并集list2.removeAll(list1);list1.addAll(list2);// 交集list1.retainAll(list2);// 差集list1.removeAll(list2); } |
Stream流取并集的幾種方式
| List<YearMonthBo> list1 = new ArrayList<YearMonthBo>(); list1.add(new YearMonthBo(2020, 1)); list1.add(new YearMonthBo(2020, 2)); list1.add(new YearMonthBo(2019, 12));List<YearMonthBo> list2 = new ArrayList<YearMonthBo>(); list2.add(new YearMonthBo(2020, 2)); list2.add(new YearMonthBo(2020, 3)); list2.add(new YearMonthBo(2019, 11)); // 1:集合并集 List<YearMonthBo> collect = Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toList()); // 2:去重取并集 Set<YearMonthBo> collect1 = Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toSet()); // 3:合并去重 List<YearMonthBo> collect2 = Stream.of(list1, list2).flatMap(List::stream).distinct().collect(Collectors.toList()); // 4:合并并根據(jù)對象中的某些字段去重(相乘相加等操作均可) ArrayList<YearMonthBo> yearMonthBoArrayList = Lists.newArrayList(Stream.of(list1, list2).flatMap(List::stream).collect(Collectors.toMap(YearMonthBo::getYear, Function.identity(), (x, y) -> x == null ? y : x)).values()); // 5:集合合并 List<YearMonthBo> collect3 = Stream.concat(list1.stream(), list2.stream()).collect(Collectors.toList()); |
集合排序
| // 1:根據(jù)compare接口排序,需實現(xiàn)Comparable接口 Collections.sort(testList); // 2:根據(jù)自定義比較器排序 Collections.sort(testList, YearMonthBo::compareTo); // 3:根據(jù)自定義排序器排序 testList.sort(Comparator.comparingInt(YearMonthBo::getYear)); |
集合 轉(zhuǎn)換為map
| // collectors方法 tomap Map<Integer, YearMonthBo> map2 = testList.stream().collect(Collectors.toMap(YearMonthBo::getYear, Function.identity(), (a1, a2) -> new YearMonthBo(a1.getYear() + a2.getYear(), a2.getMonth() + a1.getMonth()))); // collectors方法 grouping by Map<Integer, List<YearMonthBo>> map3 = testList.stream().collect(Collectors.groupingBy(YearMonthBo::getYear)); // guava tomap Map<Integer, YearMonthBo> map1 = Maps.uniqueIndex(testList, YearMonthBo::getYear); |
集合去重
| List<YearMonthBo> list1 = new ArrayList<YearMonthBo>(); list1.add(new YearMonthBo(2020, 1)); list1.add(new YearMonthBo(2020, 2)); list1.add(new YearMonthBo(2019, 12));List<YearMonthBo> list2 = new ArrayList<YearMonthBo>(); list2.add(new YearMonthBo(2020, 2)); list2.add(new YearMonthBo(2020, 3)); list2.add(new YearMonthBo(2019, 11)); // 1:根據(jù)equals和hashcode去重 List<YearMonthBo> collect4 = Stream.of(list1, list2).flatMap(List::stream).distinct().collect(Collectors.toList()); // 2:”根據(jù)對象屬性去重 Collection<YearMonthBo> values = Stream.of(list1).flatMap(List::stream).collect(Collectors.toMap(YearMonthBo::getYear, Function.identity(), (x, y) -> x == null ? y : x)).values(); // 3:根據(jù)對象屬性去重 List<YearMonthBo> collect5 = Stream.of(list1, list2).flatMap(List::stream).filter(distinctByKey(YearMonthBo::getYear)).collect(Collectors.toList()); |
集合反轉(zhuǎn)
| public static void main(String[] args) {List<String> list1 = Lists.newArrayList("a","b","c");List<String> list2 = new ArrayList<String>();list2.add("C");list2.add("B");list2.add("D");// guava反轉(zhuǎn)Lists.reverse(list1 );// java反轉(zhuǎn)for循環(huán)反轉(zhuǎn) } |
笛卡爾積
| HashSet<Integer> set = Sets.newHashSet(1, 4, 3); HashSet<Integer> set1 = Sets.newHashSet(1, 9, 8); Set<List<Integer>> lists = Sets.cartesianProduct(set, set1); System.out.println(lists); |
字符串集合拼接(空分隔符,帶分隔符,帶分隔符和前綴后綴)
| List<String> strList = new ArrayList<>(); strList.addAll(Lists.newArrayList("aa", "vv", "ww")); String collect = strList.stream().collect(Collectors.joining()); String collect = strList.stream().collect(Collectors.joining(",")); String collect = strList.stream().collect(Collectors.joining(",","prefix","suffix")); |
初始化
java集合初始化
| 1 | ArrayList<String> objects1 = new ArrayList<>(); | ? |
| 2 | ArrayList<String> objects1 = new ArrayList<>(cap); | ? |
| 3 | ArrayList<String> objects1 = new ArrayList<>(Collection); | ? |
guava集合初始化
| 1 | HashSet<Integer> integers = Sets.newHashSet(1, 2, 4); | 自動識別泛型類型 |
| 2 | HashSet<Object> objects = Sets.newHashSet(); | ? |
| 3 | Sets.newHashSetWithExpectedSize(5); | ? |
| 4 | Sets.newHashSet(Itearable); | ? |
紅黑樹
簡介:
紅黑樹是一種自平衡排序二叉樹,樹中每個節(jié)點的值,都大于或等于在它的左子樹中的所有節(jié)點的值,并且小于或等于在它的右子樹中的所有節(jié)點的值,這確保紅黑樹運行時可以快速地在樹中查找和定位的所需節(jié)點,是一種特殊的排序二叉樹。
? ? 排序二叉樹要么是一棵空二叉樹,要么是具有下列性質(zhì)的二叉樹:
? ? ? ? 若它的左子樹不空,則左子樹上所有節(jié)點的值均小于它的根節(jié)點的值;
? ? ? ? 若它的右子樹不空,則右子樹上所有節(jié)點的值均大于它的根節(jié)點的值;
? ? ? ? 它的左、右子樹也分別為排序二叉樹。
排序二叉樹的構(gòu)成:每當(dāng)程序希望添加新節(jié)點時:系統(tǒng)總是從樹的根節(jié)點開始比較 ,即將根節(jié)點當(dāng)成當(dāng)前節(jié)點,如果新增節(jié)點大于當(dāng)前節(jié)點、并且當(dāng)前節(jié)點的右子節(jié)點存在,則以右子節(jié)點作為當(dāng)前節(jié)點;如果新增節(jié)點小于當(dāng)前節(jié)點、并且當(dāng)前節(jié)點的左子節(jié)點存在,則以左子節(jié)點作為當(dāng)前節(jié)點;如果新增節(jié)點等于當(dāng)前節(jié)點,則用新增節(jié)點覆蓋當(dāng)前節(jié)點,并結(jié)束循環(huán) —— 直到找到某個節(jié)點的左、右子節(jié)點不存在,將新節(jié)點添加該節(jié)點的子節(jié)點 —— 如果新節(jié)點比該節(jié)點大,則添加為右子節(jié)點;如果新節(jié)點比該節(jié)點小,則添加為左子節(jié)點。
紅黑樹的定義:
? ? 性質(zhì) 1:每個節(jié)點要么是紅色,要么是黑色。
? ? 性質(zhì) 2:根節(jié)點永遠是黑色的。
? ? 性質(zhì) 3:所有的葉節(jié)點都是空節(jié)點(即 null),并且是黑色的。
? ? 性質(zhì) 4:每個紅色節(jié)點的兩個子節(jié)點都是黑色。(從每個葉子到根的路徑上不會有兩個連續(xù)的紅色節(jié)點)
? ? 性質(zhì) 5:從任一節(jié)點到其子樹中每個葉子節(jié)點的路徑都包含相同數(shù)量的黑色節(jié)點。
根據(jù)性質(zhì) 5:紅黑樹從根節(jié)點到每個葉子節(jié)點的路徑都包含相同數(shù)量的黑色節(jié)點,因此從根節(jié)點到葉子節(jié)點的路徑中包含的黑色節(jié)點數(shù)被稱為樹的“黑色高度(black-height)”。
性質(zhì) 4 則保證了從根節(jié)點到葉子節(jié)點的最長路徑的長度不會超過任何其他路徑的兩倍。假如有一棵黑色高度為 3 的紅黑樹:從根節(jié)點到葉節(jié)點的最短路徑長度是 2,該路徑上全是黑色節(jié)點(黑節(jié)點 - 黑節(jié)點 - 黑節(jié)點)。最長路徑也只可能為 4,在每個黑色節(jié)點之間插入一個紅色節(jié)點(黑節(jié)點 - 紅節(jié)點 - 黑節(jié)點 - 紅節(jié)點 - 黑節(jié)點),性質(zhì) 4 保證絕不可能插入更多的紅色節(jié)點。由此可見,紅黑樹中最長路徑就是一條紅黑交替的路徑。由此我們可以得出結(jié)論:對于給定的黑色高度為 N 的紅黑樹,從根到葉子節(jié)點的最短路徑長度為 N-1,最長路徑長度為 2 * (N-1)。
提示:排序二叉樹的深度直接影響了檢索的性能,正如前面指出,當(dāng)插入節(jié)點本身就是由小到大排列時,排序二叉樹將變成一個鏈表,這種排序二叉樹的檢索性能最低:N 個節(jié)點的二叉樹深度就是 N-1。
紅黑樹通過上面這種限制來保證它大致是平衡的——因為紅黑樹的高度不會無限增高,這樣保證紅黑樹在最壞情況下都是高效的,不會出現(xiàn)普通排序二叉樹的情況。
由于紅黑樹只是一個特殊的排序二叉樹,因此對紅黑樹上的只讀操作與普通排序二叉樹上的只讀操作完全相同,只是紅黑樹保持了大致平衡,因此檢索性能比排序二叉樹要好很多。
但在紅黑樹上進行插入操作和刪除操作會導(dǎo)致樹不再符合紅黑樹的特征,因此插入操作和刪除操作都需要進行一定的維護,以保證插入節(jié)點、刪除節(jié)點后的樹依然是紅黑樹。
插入節(jié)點后的修復(fù)
?
刪除節(jié)點后的修復(fù)
五:實踐
集合使用規(guī)范
集合對比
| ArrayList | 有序 | 是 | 不安全 | 可以 | ? | ? |
| LinkList | 有序 | 是 | 不安全 | 可以 | ? | ? |
| HashSet | 無序 | 否 | 不安全 | 可以 | ? | ? |
| TreeSet | 有序 | 否 | 不安全 | 不可以 | ? | ? |
| HashMap | 無序 | Key唯一 value可重復(fù) | 不安全 | key value均可以 | ? | ? |
| TreeMap | 有序 | Key唯一 value可重復(fù) | 不安全 | value可以 key不可以 | ? | ? |
| HashTable | 無序 | Key唯一 value可重復(fù) | 安全 | 不可以 | ? | ? |
| ConcurrentHashMap | 無序 | Key唯一 value可重復(fù) | 安全 | 不可以 | ? | ? |
| Vector | 有序 | 是 | 安全 | 可以 | ? | ? |
| ? | ? | ? | ? | ? | ? | ? |
區(qū)別與適用場景:
? ? Set和List對比:?
? ??? ??Set:檢索元素效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變。
? ? ? ??適用場景:頻繁對集合元素進行增刪操作的場景;需要保證數(shù)據(jù)不重復(fù)的場景。
? ??? ??List:和數(shù)組類似,List可以動態(tài)增長,查找元素效率高,插入刪除元素效率低,因為會引起其他元素位置改變。?
?? ? ? ?適用場景:查詢較為頻繁的場景;元素可重復(fù);需在確定索引上進行增刪操作的場景。
? ? ArrayList和LinkList的對比:
????? ? ArrayList: 隨機訪問get和set優(yōu)于linklist,因為linklist要移動指針。
? ? ??? LinkList:對于add和remove操作優(yōu)于arraylist,因為arraylist要移動數(shù)據(jù)。(一般情況下)
利用set集合去重
| /*** 獲取對應(yīng)三級部門下的采購員*/ public Set<PurchaserVo> getPurControllerByDept(Integer deptId3) {// 根據(jù)三級部門獲取采購/銷售/部門/品牌/品類關(guān)系List<CateBrandPurSalerRelVo> relVoList = sccAuthJsfService.getCateBrandPurSalerRelByDept3(deptId3);// 部門下不存在權(quán)限關(guān)系則記錄異常日志,返回空關(guān)系集合if (CollectionUtils.isEmpty(relVoList)) {log.error("三級部門:{}下權(quán)限關(guān)系數(shù)據(jù)為空!", deptId3);return Collections.emptySet();}// 將權(quán)限關(guān)系放到set集合中,自動去重Set<PurchaserVo> purchaserVoSet = Sets.newHashSet();relVoList.forEach(relVo -> {PurchaserVo purchaserVo = new PurchaserVo();purchaserVo.setPurchaserErp(relVo.getPurchaserControlErp());purchaserVo.setPurchaserName(relVo.getPurchaserControlName());purchaserVoSet.add(purchaserVo);});return purchaserVoSet; } |
排序,去重,根據(jù)某字段排序
| /*** 合并去重排序*/ private List<CommonDropdownDto> unionAndDistinct2List(List<CommonDropdownDto> purDataList,List<CommonDropdownDto> purDataInDb) {return Stream.concat(purDataList.stream(), purDataInDb.stream()).distinct().collect(Collectors.collectingAndThen(Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(o -> o.getValue()))),ArrayList::new)); } |
集合轉(zhuǎn)換MAP
集合轉(zhuǎn)換為map時可以使用Collectors類中的toMap方法,但是需要注意,如果有重復(fù)的鍵或者值時,會拋出異常。需自己傳入處理重復(fù)的方法。 (也可轉(zhuǎn)換為線程安全的map :toConcurrentMap)
也可以使用GroupingBy進行分組。
| List<YearMonthBo> yearMonthBoList = Lists.newArrayList(); yearMonthBoList.add(new YearMonthBo(2020, 11)); yearMonthBoList.add(new YearMonthBo(2020, 12)); yearMonthBoList.add(new YearMonthBo(2020, 11)); // Exception in thread "main" java.lang.IllegalStateException: Duplicate key 11 Map<YearMonthBo, Integer> map1 = yearMonthBoList.stream().collect(Collectors.toMap(Function.identity(), YearMonthBo::getMonth)); // success Map<YearMonthBo, Integer> map2 = yearMonthBoList.stream().collect(Collectors.toMap(Function.identity(), YearMonthBo::getMonth, Integer::sum)); // success Map<Integer, List<YearMonthBo>> map4 = yearMonthBoList.stream().collect(Collectors.groupingBy(yearMonthBo -> yearMonthBo.getYear())); |
?
總結(jié)
以上是生活随笔為你收集整理的JAVA集合和guava集合使用和原理解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu防火墙:ufw
- 下一篇: 2个字段并在一次插入一个字段里面_ela