java集合里面的函數(shù)
網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、重慶小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了蘭州免費(fèi)建站歡迎大家使用!
java集合里面的函數(shù)_java集合【1】——— 從集合接口框架說(shuō)起
百里方欣
原創(chuàng)
關(guān)注
0點(diǎn)贊·155人閱讀
(一) java集合分類(lèi)
之前大概分為三種,Set,List,Map三種,JDK5之后,增加Queue.主要由Collection和Map兩個(gè)接口衍生出來(lái),同時(shí)Collection接口繼承Iterable接口,所以我們也可以說(shuō)java里面的集合類(lèi)主要是由Iterable和Map兩個(gè)接口以及他們的子接口或者其實(shí)現(xiàn)類(lèi)組成。我們可以認(rèn)為Collection接口定義了單列集合的規(guī)范,每次只能存儲(chǔ)一個(gè)元素,而Map接口定義了雙列集合的規(guī)范,每次能存儲(chǔ)一對(duì)元素。
Iterable接口:主要是實(shí)現(xiàn)遍歷功能
Collection接口: 允許重復(fù)
Set接口:無(wú)序,元素不可重復(fù),訪問(wèn)元素只能通過(guò)元素本身來(lái)訪問(wèn)。
List接口:有序且可重復(fù),可以根據(jù)元素的索引來(lái)訪問(wèn)集合中的元素。
Queue接口:隊(duì)列集合
Map接口:映射關(guān)系,簡(jiǎn)單理解為鍵值對(duì),Key不可重復(fù),與Collection接口關(guān)系不大,只是個(gè)別函數(shù)使用到。
整個(gè)接口框架關(guān)系如下(來(lái)自百度百科):
(1) Iterable接口
1. 內(nèi)部定義的方法
java集合最源頭的接口,實(shí)現(xiàn)這個(gè)接口的作用主要是集合對(duì)象可以通過(guò)迭代器去遍歷每一個(gè)元素。
源碼如下:
// 返回一個(gè)內(nèi)部元素為T(mén)類(lèi)型的迭代器(JDK1.5只有這個(gè)接口)
Iterator iterator();
// 遍歷內(nèi)部元素,action意思為動(dòng)作,指可以對(duì)每個(gè)元素進(jìn)行操作(JDK1.8添加)
default void forEach(Consumer super T action) {}
// 創(chuàng)建并返回一個(gè)可分割迭代器(JDK1.8添加),分割的迭代器主要是提供可以并行遍歷元素的迭代器,可以適應(yīng)現(xiàn)在cpu多核的能力,加快速度。
default Spliterator spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
從上面可以看出,foreach迭代以及可分割迭代,都加了default關(guān)鍵字,這個(gè)是Java 8 新的關(guān)鍵字,以前接口的所有接口,具體子類(lèi)都必須實(shí)現(xiàn),而對(duì)于deafult關(guān)鍵字標(biāo)識(shí)的方法,其子類(lèi)可以不用實(shí)現(xiàn),這也是接口規(guī)范發(fā)生變化的一點(diǎn)。
下面我們分別展示三個(gè)接口的調(diào)用:
1.1 iterator方法
public static void iteratorHasNext(){
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
// 返回迭代器
Iterator iterator=list.iterator();
// hashNext可以判斷是否還有元素
while(iterator.hasNext()){
//next()作用是返回當(dāng)前指針指向的元素,之后將指針移向下個(gè)元素
System.out.println(iterator.next());
}
}
當(dāng)然也可以使用for-each loop方式遍歷
for (String item : list) {
System.out.println(item);
}
但是實(shí)際上,這種寫(xiě)法在class文件中也是會(huì)轉(zhuǎn)成迭代器形式,這只是一個(gè)語(yǔ)法糖。class文件如下:
public class IterableTest {
public IterableTest() { }
public static void main(String[] args) {
iteratorHasNext();
}
public static void iteratorHasNext() {
List list = new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
Iterator iterator = list.iterator();
Iterator var2 = list.iterator();
while(var2.hasNext()) {
String item = (String)var2.next();
System.out.println(item);
}
}
}
需要注意的一點(diǎn)是,迭代遍歷的時(shí)候,如果刪除或者添加元素,都會(huì)拋出修改異常,這是由于快速失敗【fast-fail】機(jī)制。
public static void iteratorHasNext(){
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
for (String item : list) {
if(item.equals("Jam")){
list.remove(item);
}
System.out.println(item);
}
}
從下面的錯(cuò)誤我們可以看出,第一個(gè)元素是有被打印出來(lái)的,也就是remove操作是成功的,只是遍歷到第二個(gè)元素的時(shí)候,迭代器檢查,發(fā)現(xiàn)被改變了,所以拋出了異常。
Jam
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at IterableTest.iteratorHasNext(IterableTest.java:15)
at IterableTest.main(IterableTest.java:7)
1.2 forEach方法
其實(shí)就是把對(duì)每一個(gè)元素的操作當(dāng)成了一個(gè)對(duì)象傳遞進(jìn)來(lái),對(duì)每一個(gè)元素進(jìn)行處理。
default void forEach(Consumer super T action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
```java
當(dāng)然像ArrayList自然也是有自己的實(shí)現(xiàn)的,那我們就可以使用這樣的寫(xiě)法,簡(jiǎn)潔優(yōu)雅。forEach方法在java8中參數(shù)是`java.util.function.Consumer`,可以稱為**消費(fèi)行為**或者說(shuō)**動(dòng)作**類(lèi)型。
```java
list.forEach(x - System.out.print(x));
同時(shí),我們只要實(shí)現(xiàn)Consumer接口,就可以自定義動(dòng)作,如果不自定義,默認(rèn)迭代順序是按照元素的順序。
public class ConsumerTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add("Jam");
list.add("Jane");
list.add("Sam");
MyConsumer myConsumer = new MyConsumer();
Iterator it = list.iterator();
list.forEach(myConsumer);
}
static class MyConsumer implements Consumer {
@Override
public void accept(Object t) {
System.out.println("自定義打?。? + t);
}
}
}
輸出的結(jié)果:
自定義打?。篔am
自定義打?。篔ane
自定義打?。篠am
1.3 spliterator方法
這是一個(gè)為了并行遍歷數(shù)據(jù)元素而設(shè)計(jì)的迭代方法,返回的是Spliterator,是專門(mén)并行遍歷的迭代器。以發(fā)揮多核時(shí)代的處理器性能,java默認(rèn)在集合框架中提供了一個(gè)默認(rèn)的Spliterator實(shí)現(xiàn),底層也就是Stream.isParallel()實(shí)現(xiàn)的,我們可以看一下源碼:
// stream使用的就是spliterator
default Stream stream() {
return StreamSupport.stream(spliterator(), false);
}
default Spliterator spliterator() {
return Spliterators.spliterator(this, 0);
}
public static Stream stream(Spliterator spliterator, boolean parallel) {
Objects.requireNonNull(spliterator);
return new ReferencePipeline.Head(spliterator,
StreamOpFlag.fromCharacteristics(spliterator),
parallel);
}
使用的方法如下:
public static void spliterator(){
List list = Arrays.asList("1", "2", "3","4","5","6","7","8","9","10");
// 獲取可迭代器
Spliterator spliterator = list.spliterator();
// 一個(gè)一個(gè)遍歷
System.out.println("tryAdvance: ");
spliterator.tryAdvance(item-System.out.print(item+" "));
spliterator.tryAdvance(item-System.out.print(item+" "));
System.out.println("\n-------------------------------------------");
// 依次遍歷剩下的
System.out.println("forEachRemaining: ");
spliterator.forEachRemaining(item-System.out.print(item+" "));
System.out.println("\n------------------------------------------");
// spliterator1:0~10
Spliterator spliterator1 = list.spliterator();
// spliterator1:6~10 spliterator2:0~5
Spliterator spliterator2 = spliterator1.trySplit();
// spliterator1:8~10 spliterator3:6~7
Spliterator spliterator3 = spliterator1.trySplit();
System.out.println("spliterator1: ");
spliterator1.forEachRemaining(item-System.out.print(item+" "));
System.out.println("\n------------------------------------------");
System.out.println("spliterator2: ");
spliterator2.forEachRemaining(item-System.out.print(item+" "));
System.out.println("\n------------------------------------------");
System.out.println("spliterator3: ");
spliterator3.forEachRemaining(item-System.out.print(item+" "));
}
tryAdvance() 一個(gè)一個(gè)元素進(jìn)行遍歷
forEachRemaining() 順序地分塊遍歷
trySplit()進(jìn)行分區(qū)形成另外的 Spliterator,使用在并行操作中,分出來(lái)的是前面一半,就是不斷把前面一部分分出來(lái)
結(jié)果如下:
tryAdvance:
1 2
-------------------------------------------
forEachRemaining:
3 4 5 6 7 8 9 10
------------------------------------------
spliterator1:
8 9 10
------------------------------------------
spliterator2:
1 2 3 4 5
------------------------------------------
spliterator3:
6 7
還有一些其他的用法在這里就不列舉了,主要是trySplit()之后,可以用于多線程遍歷。理想的時(shí)候,可以平均分成兩半,有利于并行計(jì)算,但是不是一定平分的。
2. Collection接口 extend Iterable
Collection接口可以算是集合類(lèi)的一個(gè)根接口之一,一般不能夠直接使用,只是定義了一個(gè)規(guī)范,定義了添加,刪除等管理數(shù)據(jù)的方法。繼承Collection接口的有List,Set,Queue,不過(guò)Queue定義了自己的一些接口,相對(duì)來(lái)說(shuō)和其他的差異比較大。
2.1 內(nèi)部定義的方法
源碼如下:
boolean add(Object o) //添加元素
boolean remove(Object o) //移除元素
boolean addAll(Collection c) //批量添加
boolean removeAll(Collection c) //批量移除
void retainAll(Collection c) // 移除在c中不存在的元素
void clear() //清空集合
int size() //集合大小
boolean isEmpty() //是否為空
boolean contains(Object o) //是否包含在集合中
boolean containsAll(Collection c) //是否包含所有的元素
Iterator iterator() // 獲取迭代器
Object[] toArray() // 轉(zhuǎn)成數(shù)組
default boolean removeIf(Predicate super E filter) {} // 刪除集合中復(fù)合條件的元素,刪除成功返回true
boolean equals(Object o)
int hashCode()
default Spliterator spliterator() {} //獲取可分割迭代器
default Stream stream() {} //獲取流
default Stream parallelStream() {} //獲取并行流
里面獲取并行流的方法parallelStream(),其實(shí)就是通過(guò)默認(rèn)的ForkJoinPool(主要用來(lái)使用分治法(Divide-and-Conquer Algorithm)來(lái)解決問(wèn)題),提高多線程任務(wù)的速度。我們可以使用ArrayList來(lái)演示一下平行處理能力。例如下面的例子,輸出的順序就不一定是1,2,3...,可能是亂序的,這是因?yàn)槿蝿?wù)會(huì)被分成多個(gè)小任務(wù),任務(wù)執(zhí)行是沒(méi)有特定的順序的。
List list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
list.parallelStream()
.forEach(out::println);
2.2 繼承Collection的主要接口
graph LR;
Collection --List-有順序,可重復(fù)
List-有順序,可重復(fù) --LinkedList-使用鏈表實(shí)現(xiàn),線程不安全
List-有順序,可重復(fù) --ArrayList-數(shù)組實(shí)現(xiàn),線程不安全
List-有順序,可重復(fù) --Vector-數(shù)組實(shí)現(xiàn),線程安全
Vector-數(shù)組實(shí)現(xiàn),線程安全 --Stack-堆棧,先進(jìn)后出
Collection--Set-不可重復(fù),內(nèi)部排序
Set-不可重復(fù),內(nèi)部排序--HashSet-hash表存儲(chǔ)
HashSet-hash表存儲(chǔ)--LinkHashSet-鏈表維護(hù)插入順序
Set-不可重復(fù),內(nèi)部排序--TreeSet-二叉樹(shù)實(shí)現(xiàn),排序
Collection--Queue-隊(duì)列,先進(jìn)先出
2.2.1 List extend Collection
繼承于Collection接口,有順序,取出的順序與存入的順序一致,有索引,可以根據(jù)索引獲取數(shù)據(jù),允許存儲(chǔ)重復(fù)的元素,可以放入為null的元素。
最常見(jiàn)的三個(gè)實(shí)現(xiàn)類(lèi)就是ArrayList,Vector,LinkedList,ArrayList和Vector都是內(nèi)部封裝了對(duì)數(shù)組的操作,唯一不同的是,Vector是線程安全的,而ArrayList不是,理論上ArrayList操作的效率會(huì)比Vector好一些。
里面是接口定義的方法:
int size(); //獲取大小
boolean isEmpty(); //判斷是否為空
boolean contains(Object o); //是否包含某個(gè)元素
Iterator iterator(); //獲取迭代器
Object[] toArray(); // 轉(zhuǎn)化成為數(shù)組(對(duì)象)
T[] toArray(T[] a); // 轉(zhuǎn)化為數(shù)組(特定位某個(gè)類(lèi))
boolean add(E e); //添加
boolean remove(Object o); //移除元素
boolean containsAll(Collection c); // 是否包含所有的元素
boolean addAll(Collection extends E c); //批量添加
boolean addAll(int index, Collection extends E c); //批量添加,指定開(kāi)始的索引
boolean removeAll(Collection c); //批量移除
boolean retainAll(Collection c); //將c中不包含的元素移除
default void replaceAll(UnaryOperator operator) {}//替換
default void sort(Comparator super E c) {}// 排序
void clear();//清除所有的元素
boolean equals(Object o);//是否相等
int hashCode(); //計(jì)算獲取hash值
E get(int index); //通過(guò)索引獲取元素
E set(int index, E element);//修改元素
void add(int index, E element);//在指定位置插入元素
E remove(int index);//根據(jù)索引移除某個(gè)元素
int indexOf(Object o); //根據(jù)對(duì)象獲取索引
int lastIndexOf(Object o); //獲取對(duì)象元素的最后一個(gè)元素
ListIterator listIterator(); // 獲取List迭代器
ListIterator listIterator(int index); // 根據(jù)索引獲取當(dāng)前的位置的迭代器
List subList(int fromIndex, int toIndex); //截取某一段數(shù)據(jù)
default Spliterator spliterator(){} //獲取可切分迭代器
上面的方法都比較簡(jiǎn)單,值得一提的是里面出現(xiàn)了ListIterator,這是一個(gè)功能更加強(qiáng)大的迭代器,繼承于Iterator,只能用于List類(lèi)型的訪問(wèn),拓展功能例如:通過(guò)調(diào)用listIterator()方法獲得一個(gè)指向List開(kāi)頭的ListIterator,也可以調(diào)用listIterator(n)獲取一個(gè)指定索引為n的元素的ListIterator,這是一個(gè)可以雙向移動(dòng)的迭代器。
操作數(shù)組索引的時(shí)候需要注意,由于List的實(shí)現(xiàn)類(lèi)底層很多都是數(shù)組,所以索引越界會(huì)報(bào)錯(cuò)IndexOutOfBoundsException。
說(shuō)起List的實(shí)現(xiàn)子類(lèi):
ArrayList:底層存儲(chǔ)結(jié)構(gòu)是數(shù)組結(jié)構(gòu),增加刪除比較慢,查找比較快,是最常用的List集合。線程不安全。
LinkedList:底層是鏈表結(jié)構(gòu),增加刪除比較快,但是查找比較慢。線程不安全。
Vector:和ArrayList差不多,但是是線程安全的,即同步。
2.2.2 Set extend Collection
Set接口,不允許放入重復(fù)的元素,也就是如果相同,則只存儲(chǔ)其中一個(gè)。
下面是源碼方法:
int size(); //獲取大小
boolean isEmpty(); //是否為空
boolean contains(Object o); //是否包含某個(gè)元素
Iterator iterator(); //獲取迭代器
Object[] toArray(); //轉(zhuǎn)化成為數(shù)組
T[] toArray(T[] a); //轉(zhuǎn)化為特定類(lèi)的數(shù)組
boolean add(E e); //添加元素
boolean remove(Object o); //移除元素
boolean containsAll(Collection c); //是否包含所有的元素
boolean addAll(Collection extends E c); //批量添加
boolean retainAll(Collection c); //移除所有不存在于c集合中的元素
boolean removeAll(Collection c); //移除所有在c集合中存在的元素
void clear(); //清空集合
boolean equals(Object o); //是否相等
int hashCode(); //計(jì)算hashcode
default Spliterator spliterator() {} //獲取可分割迭代器
主要的子類(lèi):
HashSet
允許空值
通過(guò)HashCode方法計(jì)算獲取hash值,確定存儲(chǔ)位置,無(wú)序。
LinkedHashSet
HashSet的子類(lèi)
有順序
TreeSet
如果無(wú)參數(shù)構(gòu)建Set,則需要實(shí)現(xiàn)Comparable方法。
亦可以創(chuàng)建時(shí)傳入比較方法,用于排序。
2.2.3 Queue extend Collection
隊(duì)列接口,在Collection接口的接觸上添加了增刪改查接口定義,一般默認(rèn)是先進(jìn)先出,即FIFO,除了優(yōu)先隊(duì)列和棧,優(yōu)先隊(duì)列是自己定義了排序的優(yōu)先順序,隊(duì)列中不允許放入null元素。
下面是源碼:
boolean add(E e); //插入一個(gè)元素到隊(duì)列,失敗時(shí)返回IllegalStateException (如果隊(duì)列容量不夠)
boolean offer(E e); //插入一個(gè)元素到隊(duì)列,失敗時(shí)返回false
E remove(); //移除隊(duì)列頭的元素并移除
E poll(); //返回并移除隊(duì)列的頭部元素,隊(duì)列為空時(shí)返回null
E element(); //返回隊(duì)列頭元素
E peek(); //返回隊(duì)列頭部的元素,隊(duì)列為空時(shí)返回null
主要的子接口以及實(shí)現(xiàn)類(lèi)有:
Deque(接口):Queue的子接口,雙向隊(duì)列,可以從兩邊存取
ArrayDeque:Deque的實(shí)現(xiàn)類(lèi),底層用數(shù)組實(shí)現(xiàn),數(shù)據(jù)存貯在數(shù)組中
AbstractQueue:Queue的子接口,僅實(shí)現(xiàn)了add、remove和element三個(gè)方法
PriorityQueue:按照默認(rèn)或者自己定義的順序來(lái)排序元素,底層使用堆(完全二叉樹(shù))實(shí)現(xiàn),使用動(dòng)態(tài)數(shù)組實(shí)現(xiàn),
BlockingQueue: 在java.util.concurrent包中,阻塞隊(duì)列,滿足當(dāng)前無(wú)法處理的操作。
(2) Map接口
定義雙列集合的規(guī)范Map,每次存儲(chǔ)一對(duì)元素,即key和value。
key的類(lèi)型可以和value的類(lèi)型相同,也可以不同,任意的引用類(lèi)型都可以。
key是不允許重復(fù)的,但是value是可以重復(fù)的,所謂重復(fù)是指計(jì)算的hash值系統(tǒng)。
下面的源碼的方法:
V put(K key, V value); // 添加元素
V remove(Object key); // 刪除元素
void putAll(Map extends K, ? extends V m); // 批量添加
void clear() // 移除所有元素
V get(Object key); // 通過(guò)key查詢?cè)?/p>
int size(); // 查詢集合大小
boolean isEmpty(); // 集合是否為空
boolean containsKey(Object key); // 是否包含某個(gè)key
boolean containsValue(Object value); // 是否包含某個(gè)value
Set keySet(); // 獲取所有key的set集合
Collection values(); // 獲取所有的value的set集合
Set entrySet(); // 返回鍵值對(duì)的set,每一個(gè)鍵值對(duì)是一個(gè)entry對(duì)象
boolean equals(Object o); // 用于比較的函數(shù)
int hashCode(); // 計(jì)算hashcode
default V getOrDefault(Object key, V defaultValue) // 獲取key對(duì)應(yīng)的Value,沒(méi)有則返回默認(rèn)值()
default void forEach(BiConsumer super K, ? super V action) {} // 遍歷
default void replaceAll(BiFunction super K, ? super V, ? extends V function) {} // 批量替換
// 缺少這個(gè)key的時(shí)候才會(huì)添加進(jìn)去
// 返回值是是key對(duì)應(yīng)的value值,如果不存在,則返回的是剛剛放進(jìn)去的value
default V putIfAbsent(K key, V value) {}
default boolean remove(Object key, Object value) {} // 移除元素
default boolean replace(K key, V oldValue, V newValue) {} // 替換
default V replace(K key, V value) {} //替換
// 和putIfAbsent有點(diǎn)像,只不過(guò)傳進(jìn)去的mappingFunction是映射函數(shù),也就是如果不存在key對(duì)應(yīng)的value,將會(huì)執(zhí)行函數(shù),函數(shù)返回值會(huì)被當(dāng)成value添加進(jìn)去,同時(shí)返回新的value值
default V computeIfAbsent(K key,Function super K, ? extends V mappingFunction) {}
// 和computeIfAbsent方法相反,只有key存在的時(shí)候,才會(huì)執(zhí)行函數(shù),并且返回
default V computeIfPresent(K key,BiFunction super K, ? super V, ? extends V remappingFunction) {}
// 不管如何都會(huì)執(zhí)行映射函數(shù),返回value
default V compute(K key,BiFunction super K, ? super V, ? extends V remappingFunction) {}
default V merge(K key, V value,BiFunction super V, ? super V, ? extends V remappingFunction) {}
值得注意的是,Map里面定義了一個(gè)Entry類(lèi),其實(shí)就是定義了一個(gè)存儲(chǔ)數(shù)據(jù)的類(lèi)型,一個(gè)entry就是一個(gè).
Map的常用的實(shí)現(xiàn)子類(lèi):
HashMap:由數(shù)組和鏈表組成,線程不安全,無(wú)序。
LinkedHashMap:如果我們需要是有序的,那么就需要它,時(shí)間和空間效率沒(méi)有HashMap那么高,底層是維護(hù)一條雙向鏈表,保證了插入的順序。
ConcurrentHashMap:線程安全,1.7JDK使用鎖分離,每一段Segment都有自己的獨(dú)立鎖,相對(duì)來(lái)說(shuō)效率也比較高。JDK1.8拋棄了Segment,使用Node數(shù)組+鏈表和紅黑樹(shù)實(shí)現(xiàn),在線程安全控制上使用Synchronize和CAS,可以認(rèn)為是優(yōu)化的線程安全的HashMap。
HashTable:對(duì)比與HashMap主要是使用關(guān)鍵字synchronize,加上同步鎖,線程安全。
(二)總結(jié)
這些集合原始接口到底是什么?為什么需要?
我想,這些接口其實(shí)都是一種規(guī)則/規(guī)范的定義,如果不這么做也可以,所有的子類(lèi)自己實(shí)現(xiàn),但是從迭代以及維護(hù)的角度來(lái)說(shuō),這就是一種抽象或者分類(lèi),比如定義了Iterator接口,某一些類(lèi)就可以去繼承或者實(shí)現(xiàn),那就得遵守這個(gè)規(guī)范/契約??梢杂兴卣梗總€(gè)子類(lèi)的拓展不一樣,所以每個(gè)類(lèi)就各有所長(zhǎng),但是都有一個(gè)中心,就是原始的集合接口。比如實(shí)現(xiàn)Map接口的所有類(lèi)的中心思想都不變,只是各有所長(zhǎng),各分千秋,形成了大千集合世界。
【作者簡(jiǎn)介】:
秦懷,公眾號(hào)【秦懷雜貨店】作者,技術(shù)之路不在一時(shí),山高水長(zhǎng),縱使緩慢,馳而不息。個(gè)人寫(xiě)作方向:Java源碼解析,JDBC,Mybatis,Spring,redis,分布式,劍指Offer,LeetCode等,認(rèn)真寫(xiě)好每一篇文章,不喜歡標(biāo)題黨,不喜歡花里胡哨,大多寫(xiě)系列文章,不能保證我寫(xiě)的都完全正確,但是我保證所寫(xiě)的均經(jīng)過(guò)實(shí)踐或者查找資料。遺漏或者錯(cuò)誤之處,還望指正。
平日時(shí)間寶貴,只能使用晚上以及周末時(shí)間學(xué)習(xí)寫(xiě)作,關(guān)注我,我們一起成長(zhǎng)吧~
1) System.out.println(list);
2) [Hello,Java,Learn,World]
3)改第一句List list=new LinkedList();
1. ArrayList是實(shí)現(xiàn)了基于動(dòng)態(tài)數(shù)組的數(shù)據(jù)結(jié)構(gòu),LinkedList基于鏈表的數(shù)據(jù)結(jié)構(gòu)。
2. 對(duì)于隨機(jī)訪問(wèn)get和set,ArrayList覺(jué)得優(yōu)于LinkedList,因?yàn)長(zhǎng)inkedList要移動(dòng)指針。
3. 對(duì)于新增和刪除操作add和remove,LinedList比較占優(yōu)勢(shì),因?yàn)锳rrayList要移動(dòng)數(shù)據(jù)。
4. ArrayList的空間浪費(fèi)主要體現(xiàn)在在list列表的結(jié)尾預(yù)留一定的容量空間,而LinkedList的空間花費(fèi)則體現(xiàn)在它的每一個(gè)元素都需要消耗相當(dāng)?shù)目臻g。
4)Vector list=new Vector();
1. Vectors是可同步化的,意思就是說(shuō),任何操作Vector的內(nèi)容的方法都是線程安全的,相反的,另一方面,ArrayList是不可同步化的,所以也不是線程安全的。
2. 不管是ArrayList還是Vector,在它們內(nèi)部都是使用一個(gè)數(shù)組來(lái)保存數(shù)據(jù)的。開(kāi)發(fā)過(guò)程中,在使用它們?nèi)魏我粋€(gè)的時(shí)候,你都需要記住這一點(diǎn)。你在往一個(gè)ArrayList或者Vector里插入一個(gè)元素的時(shí)候,如果內(nèi)部數(shù)組空間不夠了,ArrayList或者Vector就要擴(kuò)展它的大小。Vector在默認(rèn)情況下是增長(zhǎng)一倍的大小,而ArrayList增加50%的大小。
了一些所謂大公司的Java面試問(wèn)題,發(fā)現(xiàn)對(duì)于JAVA集合類(lèi)的使用都比較看重似的,而自己在這方面還真的是所真甚少,抽空也學(xué)習(xí)學(xué)習(xí)吧。
java.util包中就包含了一系列重要的集合類(lèi),而對(duì)于集合類(lèi),主要需要掌握的就是它的內(nèi)部結(jié)構(gòu),以及遍歷集合的迭代模式。
接口:Collection
所有集合類(lèi)的根類(lèi)型,主要的一個(gè)接口方法:booleanadd(Ojbectc)
雖返回的是boolean,但不是表示添加成功與否,因?yàn)镃ollection規(guī)定:一個(gè)集合拒絕添加這個(gè)元素,無(wú)論什么原因,都必須拋出異常,這個(gè)返回值表示的意義是add()執(zhí)行后,集合的內(nèi)容是否改了(就是元素有無(wú)數(shù)量、位置等變化)。類(lèi)似的addAll,remove,removeAll,remainAll也是一樣的。
用Iterator模式實(shí)現(xiàn)遍歷集合
Collection有一個(gè)重要的方法:iterator(),返回一個(gè)Iterator(迭代子),用于遍歷集合的所有元素。Iterator模式可以把訪問(wèn)邏輯從不同類(lèi)的集合類(lèi)中抽象出來(lái),從而避免向客戶端暴露集合的內(nèi)部結(jié)構(gòu)。
for(Iteratorit=c.iterator();it.hasNext();){...}
不需要維護(hù)遍歷集合的“指針”,所有的內(nèi)部狀態(tài)都有Iterator來(lái)維護(hù),而這個(gè)Iterator由集合類(lèi)通過(guò)工廠方法生成。
每一種集合類(lèi)返回的Iterator具體類(lèi)型可能不同,但它們都實(shí)現(xiàn)了Iterator接口,因此,我們不需要關(guān)心到底是哪種Iterator,它只需要獲得這個(gè)Iterator接口即可,這就是接口的好處,面向?qū)ο蟮耐Α?/p>
要確保遍歷過(guò)程順利完成,電腦培訓(xùn)認(rèn)為必須保證遍歷過(guò)程中不更改集合的內(nèi)容(Iterator的remove()方法除外),所以,確保遍歷可靠的原則是:只在一個(gè)線程中使用這個(gè)集合,或者在多線程中對(duì)遍歷代碼進(jìn)行同步。
java集合的主要分為三種類(lèi)型:
Set(集)
List(列表)
Map(映射)
要深入理解集合首先要了解下我們熟悉的數(shù)組:
數(shù)組是大小固定的,并且同一個(gè)數(shù)組只能存放類(lèi)型一樣的數(shù)據(jù)(基本類(lèi)型/引用類(lèi)型),而JAVA集合可以存儲(chǔ)和操作數(shù)目不固定的一組數(shù)據(jù)。 所有的JAVA集合都位于 java.util包中! JAVA集合只能存放引用類(lèi)型的的數(shù)據(jù),不能存放基本數(shù)據(jù)類(lèi)型。
簡(jiǎn)單說(shuō)下集合和數(shù)組的區(qū)別:(參考文章:《Thinking In Algorithm》03.數(shù)據(jù)結(jié)構(gòu)之?dāng)?shù)組)
Java所有“存儲(chǔ)及隨機(jī)訪問(wèn)一連串對(duì)象”的做法,array是最有效率的一種。
1、
效率高,但容量固定且無(wú)法動(dòng)態(tài)改變。
array還有一個(gè)缺點(diǎn)是,無(wú)法判斷其中實(shí)際存有多少元素,length只是告訴我們array的容量。
2、Java中有一個(gè)Arrays類(lèi),專門(mén)用來(lái)操作array。
arrays中擁有一組static函數(shù),
equals():比較兩個(gè)array是否相等。array擁有相同元素個(gè)數(shù),且所有對(duì)應(yīng)元素兩兩相等。
fill():將值填入array中。
sort():用來(lái)對(duì)array進(jìn)行排序。
binarySearch():在排好序的array中尋找元素。
System.arraycopy():array的復(fù)制。
若撰寫(xiě)程序時(shí)不知道究竟需要多少對(duì)象,需要在空間不足時(shí)自動(dòng)擴(kuò)增容量,則需要使用容器類(lèi)庫(kù),array不適用。所以就要用到集合。
那我們開(kāi)始討論java中的集合。
集合分類(lèi):
Collection:List、Set
Map:HashMap、HashTable
Java集合是什么:
Java 中的集合類(lèi)庫(kù)可以幫助我們?cè)诔绦蛟O(shè)計(jì)中實(shí)現(xiàn)傳統(tǒng)的數(shù)據(jù)結(jié)構(gòu)。
Java的集合類(lèi)是一個(gè)用來(lái)存放對(duì)象的容器,有以下特點(diǎn):
1、Java集合只能存放對(duì)象。加入添加了一個(gè)基本數(shù)據(jù)類(lèi)型,會(huì)被自動(dòng)裝箱后存入集合。
2、集合存放的是多個(gè)對(duì)象的引用,對(duì)象本身是在堆內(nèi)存中的。
3、集合可以存放不同類(lèi)型,不限數(shù)量的數(shù)據(jù)類(lèi)型。
集合分三種:1、Set 2 、List 3、Map,下面進(jìn)行具體介紹。
擴(kuò)展鏈接:
主要內(nèi)容:
1)手寫(xiě)ArrayList
2)手寫(xiě)單鏈表
3)手寫(xiě)LinkedList
4)手寫(xiě)HashMap
5)手寫(xiě)HashSet
6)最新并發(fā)集合類(lèi)
學(xué)習(xí)目標(biāo):
1. 掌握手寫(xiě)ArrayList
2. 掌握手寫(xiě)單鏈表
3. 掌握手寫(xiě)LinkedList
4. 掌握手寫(xiě)HashMap
5. 掌握手寫(xiě)HashSet
6. 理解最新并發(fā)集合類(lèi)底層原理
視頻課程小結(jié):
01_集合提升訓(xùn)練_手寫(xiě)ArrayList_get_size_isEmpty_自定義異常
02_集合提升訓(xùn)練_手寫(xiě)ArrayList_構(gòu)造方法_add
03_集合提升訓(xùn)練_手寫(xiě)ArrayList_toString_iterator
04_集合提升循環(huán)_手寫(xiě)單鏈表_get
05_集合提升訓(xùn)練_手寫(xiě)單鏈表_add_remove_toString
06_集合提升訓(xùn)練_手寫(xiě)LinkedList
07_集合提升訓(xùn)練_手寫(xiě)LinkedList_添加內(nèi)存分配圖
08_集合提升訓(xùn)練_HashMap的原理和代碼準(zhǔn)備
09_集合提升訓(xùn)練_手寫(xiě)HashMap的put
10_集合提升訓(xùn)練_手寫(xiě)HashMap的get_toString
11_集合提升訓(xùn)練_手寫(xiě)HashSet
12_集合提升訓(xùn)練_新一代并發(fā)集合類(lèi)
ArrayList 實(shí)現(xiàn)List接口 ,隨著向 ArrayList 中不斷添加元素,其容量也自動(dòng)增長(zhǎng)
Vector向量 不過(guò)我是不太喜歡這個(gè)類(lèi)
HashMap實(shí)現(xiàn)Map接口--可以說(shuō)內(nèi)存就是一個(gè)HashMap
HashTable實(shí)現(xiàn)一個(gè)哈希表,該哈希表將鍵映射到相應(yīng)的值
Set一個(gè)不包含重復(fù)元素的容器
HashMap, HashTable都是“Key-Value對(duì)”形式的
Vector和ArrayList區(qū)別
Vector和ArrayList Vector和ArrayList在使用上非常相似,都可用來(lái)表示一組數(shù)量可變的對(duì)象應(yīng)用的集合,并且可以隨機(jī)地訪問(wèn)其中的元素。
Vector的方法都是同步的(Synchronized),是線程安全的(thread-safe),而ArrayList的方法不是,由于線程的同步必然要影響性能,因此,ArrayList的性能比Vector好。
當(dāng)Vector或ArrayList中的元素超過(guò)它的初始大小時(shí),Vector會(huì)將它的容量翻倍,而ArrayList只增加50%的大小,這樣,ArrayList就有利于節(jié)約內(nèi)存空間。
Hashtable和HashMap的區(qū)別
Hashtable和HashMap它們的性能方面的比較類(lèi)似 Vector和ArrayList,比如Hashtable的方法是同步的,而HashMap的不是。
ArrayList和LinkedList的區(qū)別
對(duì)于處理一列數(shù)據(jù)項(xiàng),Java提供了兩個(gè)類(lèi)ArrayList和LinkedList, ArrayList的內(nèi)部實(shí)現(xiàn)是基于內(nèi)部數(shù)組Object[], 所以從概念上講,它更象數(shù)組,但LinkedList的內(nèi)部實(shí)現(xiàn)是基于一組連接的記錄,所以,它更象一個(gè)鏈表結(jié)構(gòu),所以,它們?cè)谛阅苌嫌泻艽蟮牟顒e。
從上面的分析可知,在ArrayList的前面或中間插入數(shù)據(jù)時(shí),你必須將其后的所有數(shù)據(jù)相應(yīng)的后移,這樣必然要花費(fèi)較多時(shí)間,所以,當(dāng)你的操作是在一列 數(shù)據(jù)的后面添加數(shù)據(jù)而不是在前面或中間,并且需要隨機(jī)地訪問(wèn)其中的元素時(shí),使用ArrayList會(huì)提供比較好的性能
而訪問(wèn)鏈表中的某個(gè)元素時(shí),就必須從鏈表的一端開(kāi)始沿著連接方向一個(gè)一個(gè)元素地去查找,直到找到所需的元素為止,所以,當(dāng)你的操作是在一列數(shù)據(jù)的前面或中間添加或刪除數(shù)據(jù),并且按照順序訪問(wèn)其中的元素時(shí),就應(yīng)該使用LinkedList了。
如果在編程中,1,2兩種情形交替出現(xiàn),這時(shí),你可以考慮使用List這樣的通用接口,而不用關(guān)心具體的實(shí)現(xiàn),在具體的情形下,它的性能由具體的實(shí)現(xiàn)來(lái)保證。
配置集合類(lèi)的初始大小
在Java集合框架中的大部分類(lèi)的大小是可以隨著元素個(gè)數(shù)的增加而相應(yīng)的增加的,我們似乎不用關(guān)心它的初始大小,但如果我們考慮類(lèi)的性能問(wèn)題時(shí),就一定要考慮盡可能地設(shè)置好集合對(duì)象的初始大小,這將大大提高代碼的性能。
比如,Hashtable缺省的初始大小為101,載入因子為0.75,即如果其中的元素個(gè)數(shù)超過(guò)75個(gè),它就必須增加大小并重新組織元素,所以,如果你 知道在創(chuàng)建一個(gè)新的Hashtable對(duì)象時(shí)就知道元素的確切數(shù)目如為110,那么,就應(yīng)將其初始大小設(shè)為110/0.75=148,這樣,就可以避免重 新組織內(nèi)存并增加大小。