java集合----collection接口
引入
面嚮對象語言對事物的體現都是以對象的形式,爲了方便對多個對象的操作,就要對對象進行存儲。 而Java 集合就像一種容器,可以動態地 把多個對象的引用放入容器中。另外一方面,使用 Array 存儲對象方面具有 一些弊端 。
比如:
-
數組初始化以後,長度就不可變了,不便於擴展;
-
數組中提供的屬性和方法少,不便於進行添加、刪除、插入等操作, 且效率 不 高。
同時無法直接獲取存儲元素的個數; -
數組存儲的數據是有序的、可以重複的。 存儲數據的特點單一;
在java中集合框架主要分爲Collection和Map兩種體系.
這裏主要講解有關於Collection的一些操作。
Collection接口
Collection 接口是 List 、Set 和 Queue 接口的父接口,該接口裏定義的方法既可用於操作 Set 集合,也可用於操作 List 和 Queue 集合。
這裏可以看看在Java8中關於Collection接口的介紹:
翻譯爲:集合層次結構中的根接口。集合表示一組對象,稱爲其元素。有些集合允許複製元素,有些則不允許。有些是有序的,有些是無序的。JDK沒有提供該接口的任何直接實現:它提供了更具體的子接口(如Set和List)的實現。此接口通常用於在需要最大通用性的地方傳遞和操作集合。
Collection接口的方法
- add(T e): 添加一個元素(這裏T表示泛型的意思)
例子:
這裏代碼只給出了測試單元,可以看見Colletion接口add方法既可以添加int型,String型,還可以是個對象(這裏Person是我創建的一個對象)
public void test1() {
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",20));
Iterator iterator = obj.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
結果:
- addAll(Collection c ): 將指定的對象的集合加到當前集合中
例如:
@Test
public void test1() {
Collection obj = new ArrayList();
//將數組元素轉化爲list列表
Collection obj2 = Arrays.asList("123",132);
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",20));
obj.addAll(obj2);
Iterator iterator = obj.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
結果:
- int size() : 獲取有效元素的個數
例如:這裏使用size方法去返回obj中的元素個數
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
System.out.println(obj.size());
}
結果:
-void clear():清空集合
例如:使用clear方法後,我們再看看obj的size是多少
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.clear();
System.out.println(obj.size());
}
結果,毋庸置疑的是0:
- boolean isEmpty(): 判斷是否是空集合
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
boolean empty = obj.isEmpty();
System.out.println(empty);
}
結果:
- boolean contains(Object obj): 是通過元素的 equals 方法來判斷是否是同一個對象
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
boolean contains = obj.contains(123);
System.out.println(contains);
}
結果:
- boolean containsAll(Collection c) 也是調用元素的 equals 方法來比較的 。拿兩個集合的元素挨個比較 。
例如:
@Test
public void test2(){
Collection obj = new ArrayList();
Collection obj2 = Arrays.asList(123);
obj.add(123);
obj.add("AA");
boolean containsAll = obj.containsAll(obj2);
System.out.println(containsAll);
}
結果:
- boolean remove(Object obj) 通過 元素的 equals 方法判斷是否是要刪除的那個元素 。 只會刪除找到的第一個元素。
例如:
@Test
public void test3(){
Collection obj = new ArrayList();
obj.add(123);
obj.add(123);
obj.add("AA");
obj.add("BB");
obj.remove(123);
Iterator iterator = obj.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
結果:
可見並沒有把相同的元素全部刪去,只刪除第一個相同的元素。
- boolean removeAll(Collection coll 取當前集合的差集
例如:
@Test
public void test3(){
Collection obj = new ArrayList();
Collection obj2 = Arrays.asList(123,"AA");
obj.add(123);
obj.add(123);
obj.add("AA");
obj.add("BB");
obj.removeAll(obj2);
Iterator iterator = obj.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
結果:
- boolean equals(Object obj) :集合是否相等
- 這裏我們來看一個例子,我用一個Person類的一個對象,加到obj中,obj2用Arrays的方法把數組轉化爲List,其中元素也是Person類的一個對象。
person類如下所示:
package com.Collection;
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
// @Override
// public boolean equals(Object o) {
// System.out.println("Person equals()....");
// if (this == o) return true;
// if (o == null || getClass() != o.getClass()) return false;
//
// Person person = (Person) o;
//
// if (age != person.age) return false;
// return name != null ? name.equals(person.name) : person.name == null;
// }
//
// @Override
// public int hashCode() {
// int result = name != null ? name.hashCode() : 0;
// result = 31 * result + age;
// return result;
// }
}
測試單元如下:
@Test
public void test5(){
Collection obj = new ArrayList();
obj.add(new Person("LC",12));
Collection obj2 = Arrays.asList(new Person("LC",12));
boolean equals = obj.equals(obj2);
System.out.println(equals);
}
看看結果(Person類中的註釋沒打開):
現在我在Person類打開了註釋,再看看結果:
解釋:第一次判斷結果爲false,是因爲在執行equals方法的時候,調用的是Object裏面的equals方法
因爲地址值不同,所以返回false.
而第二種是自己重寫了equals方法,沒有按照地址值判斷,所以返回true
- Object[] toArray() 轉成對象數組
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
Object[] objects = obj.toArray();
System.out.println(objects[1]);
}
結果:
- hashCode(): 獲取集合對象的哈希值
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
System.out.println(obj.hashCode());
}
結果:
- iterator() 返回迭代器對象,用於集合遍歷
其實在試前面的方法的時候已經用到了。
例子:
@Test
public void test6(){
Collection obj = new ArrayList();
obj.add(123);
obj.add("AA");
obj.add(new Person("LC",12));
Iterator iterator = obj.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
結果:
注意: Collection obj = new Collection(); //錯誤,因爲接口不能實例化
Collection接口雖然不能直接被使用, 但提供了集合以及集合元素的方法且Set接口和List接口都可以調用Collection接口的方法。
Collection的子接口(一)—List接口
鑑於Java中數組用來存儲數據的侷限性,我們通常使用List替代數組。
通過源代碼可以發現List接口繼承與Collecion接口
List特點:
- List集合類中元素有序、且可重複,集合中的每個元素都有其對應的順序索引。
- List容器中的元素都對應一個整數型的序號記載其在容器中的位置,可以根據序號存取容器中的元素。
- JDK中List接口的實現類常用的有:ArrayList、LinkedList和Vector
List類的一些方法
List 除了從 Collection 集合繼承的方法外, List 集合裏添加了一些根據索引來操作集合元素的方法,下面就來了解一下它的一些方法.
這裏我用ArrayList來實現List接口中的一些方法
- void add( int index, Object o) 在 index 位置插入o元素.
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
System.out.println(list);
list.add(1,"RR");
System.out.println(list);
}
結果:
- boolean addAll(int index, Collection eles) 從 index 位置開始將 eles 中的所有元素添加進來
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
list.addAll(1, Arrays.asList("123","456"));
System.out.println(list);
}
結果:
- Object get( int index): 獲取指定 index 位置的元素
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
Object o = list.get(1);
System.out.println(o);
}
結果:
- int indexOf(Object obj):返回obj在集合中首次出現的位置,沒找到返回-1
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("BB");
list.add("BB");
int num1 = list.indexOf("BB");
int num2 = list.indexOf("Baa");
System.out.println(num1+" "+num2);
}
結果:
- int lastlndexOf(Object obj):返回obj在當前集合中末次出現的位置,沒找到返回-1
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("BB");
list.add("BB");
int num1 = list.lastIndexOf("BB");
int num2 = list.lastIndexOf("Baa");
System.out.println(num1+" "+num2);
}
結果:
- Object remove(int index):移除指定index位置的元素,並返回此元素
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
Object o = list.remove(1);
System.out.println(o);
}
結果:
- Object set(int index,Object ele):設置指定index位置的元素爲ele,並返回之前在index位置的值
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
Object o = list.set(1, "CC");
System.out.println(o);
System.out.println(list);
}
結果:
- List subList(int fromlndex,int tolndex):返回從fromlndex到tolndex位置的子集合
例子:
@Test
public void test(){
ArrayList list = new ArrayList();
list.add("AA");
list.add("BB");
list.add("CC");
list.add("DD");
List list1 = list.subList(1, 3);
System.out.println(list1);
}
結果:
List實現類之一----ArrayList
看ArrayList的源碼發現,ArrayList繼承於AbstractList, 實現了List、RandomAccess, Cloneable, java.io.Serializable。
ArrayList 是 List 接口的典型 實現類、主要實現類。本質上, ArrayList 是對象引用的 一個變長數組
ArrayList的構造方法
從構造方法中我們可以看見,默認情況下,elementData是一個大小爲0的空數組,當我們指定了初始大小的時候,elementData的初始大小就變成了我們所指定的初始大小了。
List實現類之二—LinkedList
LinkedList:雙向鏈表,內部沒有聲明數組,而是定義了Node類型的first和last,用於記錄首末元素。同時,定義內部類Node,作爲LinkedList中保存數據的基本結構。Node除了保存數據,還定義了兩個變量:
prev變量: 記錄前一個元素的位置
next變量: 記錄下一個元素的位置
對於 頻繁的插入或刪除元素 的操作,建議 使用 LinkedList 類,效率 較高
這是它的源碼:
LinkedList的一些方法
除了List中的一些方法,LinkedList還有一些方法。
- void addFirst(Object obj)
- void addLast(Object obj)
- Object getFirst()
- Object getLast()
- Object removeFirst()
- Object removeLast()
就看一個例子,來了解下這些方法:
@Test
public void test3(){
LinkedList linkedList = new LinkedList();
linkedList.add("AA");
linkedList.add("BB");
linkedList.add("CC");
linkedList.add("DD");
linkedList.add("EE");
linkedList.add("FF");
linkedList.addFirst("begin");
linkedList.addLast("last");
System.out.println(linkedList);
System.out.println("***************");
Object first = linkedList.getFirst();
Object last = linkedList.getLast();
System.out.println(first);
System.out.println(last);
Object o = linkedList.removeFirst();
Object o1 = linkedList.removeLast();
System.out.println(linkedList);
}
結果:
Vector
Vector是一個古老的集合,JDK1.0就有了。大多數操作與ArrayList相同,區別之處在於Vector是線程安全的。
在各種list中,最好把ArrayList作爲缺省選擇。當插入、刪除頻繁時,使用LinkedList;Vector總是比ArrayList慢,所以儘量避免使用。
Vector的一些方法
-
void addElement (Object obj
-
void insertElementAt (Object obj,int index)
-
void setElementAt (Object obj,int index)
-
void removeElement (Object obj
-
void removeAllElements
看一個例子:
public void test4(){
Vector vector = new Vector();
vector.addElement("aaa");
vector.addElement("aaa");
vector.addElement("bbb");
vector.insertElementAt("ccc",1);
System.out.println(vector);
vector.setElementAt("ddd",1);
System.out.println(vector);
vector.removeElement("aaa");
System.out.println(vector);
vector.removeAllElements();
System.out.println("現在還剩下元素"+vector);
}
結果:
一道關於ArrayList/LinkedList/Vector的面試題
問題:請問ArrayList/LinkedList/Vector 的 異同 談談你的理解? ArrayList 底層
是什麼?擴容機制? Vector 和 ArrayList 的最大區別。
ArrayList 和 LinkedList 的 異同二者都線程不安全,相對線程安全的Vector ,執行效率高。
此外,ArrayList 是實現了基於動態數組的數據結構, LinkedList 基於鏈表的數據結構。對於
隨機訪問 get 和 set ArrayList 覺得優於 LinkedList ,因爲 LinkedList 要移動指針。對於新增
和刪除 操作 add( 特指 插入 和 remove LinkedList 比較佔優勢,因爲 ArrayList 要移動數據。
ArrayList 和 Vector 的區別Vector和 ArrayList 幾乎是完全相同的 唯一的區別在於 Vector 是同步類 ( synchronized),屬於強同步類。因此開銷就比 ArrayList 要大,訪問要慢。正常情況下 大多數的 Java 程序員使用ArrayList 而不是 Vector, 因爲同步完全可以由程序員自己來控制。 Vector 每次擴容請求其大小的 2 倍空間,而 ArrayList 是 1.5 倍。 Vector 還有一個子 類 Stack
Collection的子接口(二)—set接口
概述
-
Set 接口 是 Collection 的子接口, set 接口沒有 提供額外 的方法
-
Set 集合不允許包含相同的元素,如果試把兩個相同的元素加入同一個
-
Set 集合中,則添加操作失敗。
-
Set 判斷兩個對象是否相同不是使用 == 運算符,而是 根據 equals() 方法
Set實現類之一— Hashset
概述
- HashSet 是 Set 接口的典型實現,大多數時候使用 Set 集合時都使用這個實現類。
- HashSet 按 Hash 算法來存儲集合中的元素,因此具有很好 的 存取、查找、刪除性能。
- HashSet具有以下特點:
不能保證元素的排列順序
HashSet不是線程安全的
集合元素可以是null - HashSet 集合判斷兩個元素相等的標準:兩個對象通過hashCode()方法比較相等,並且兩個對象的equals()方法返回值也相等。
- 對於存放在Set容器中的對象,對應的類一定要重寫equals()和hashCode(Object obj)方法,以實現對象相等規則。即:“相等的對象必須具有相等的散列碼”。
Set的無序性和不可重複型:
例子(這裏的Person類是我自己創建的):
@Test
public void test(){
HashSet set = new HashSet();
set.add("AA");
set.add("BB");
set.add("CC");
set.add("CC");
set.add(new Person("LC",20));
set.add(new Person("LC",20));
System.out.println(set);
}
猜猜結果是多少?
發現這裏把兩個Person類的對象都輸出來了
如果在重寫equals()和hashcode()方法,那會怎麼樣呢
@Override
public boolean equals(Object o) {
System.out.println("Person equals()....");
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
if (age != person.age) return false;
return name != null ? name.equals(person.name) : person.name == null;
}
@Override
public int hashCode() {
int result = name != null ? name.hashCode() : 0;
result = 31 * result + age;
return result;
}
此時輸出就只有一個Person對象了
向 HashSet 中添加元素的過程:
-
當向 HashSet 集合中存入一個元素時 ,HashSet 會調用該對象的 hashCode() 方法來得到該對象的 hashCode 值,然後根據 hashCode 值 ,通過某種散列函數決定該對象在 HashSet 底層數組中 的 存儲位置 。 這個散列函數會與底層數組的長度相計算得到在數組中的下標,並且這種散列函數計算還儘可能保證能均勻存儲元素,越是散列分佈該散列函數設計的越好(這就和數組結構有點關係了)
-
如果兩個元素的 hashCode() 值相等,會再繼續調用 equals 方法。如果 equals 方法結果爲 true 添加 失敗。如果 爲 false 那麼會保存該元素 但是該數組的位置已經有元素了那麼會通過鏈表的方式,繼續鏈接 。
-
如果 兩個元素的 equals() 方法返回 true ,但它們的 hashCode () 返回值不相等, hashSet 將會把它們存儲在不同的位置,但依然可以添加 成功。
Set實現類之二—LinkedHashset
概述
- LinkedHashSet 是HashSet的子類
- LinkedHashSet 根據元素的 hashCode值來決定元素的存儲位置,但它同時使用雙向鏈表維護元素的次序,這使得元素看起來是以插入順序保存的。
- LinkedHashSet插入性能略低於HashSet,但在迭代訪問Set裏的全部元素時有很好的性能。
- LinkedHashSet不允許集合元素重複。
看一個關於LinkedHashset的例子
@Test
public void test(){
LinkedHashSet set = new LinkedHashSet();
set.add("AA");
set.add("BB");
set.add("CC");
set.add(new Person("LC",20));
set.add("a");
set.add("b");
System.out.println(set);
}
輸出結果:
可以發現它的輸出結果順序就是添加的順序。
在Linkedhashset的底層結構
Set實現類之三—Treeset
概要:
-
TreeSet 是 SortedSet 接口的實現類, TreeSet 可以確保集合元素處於排序 狀態。
-
TreeSet 底層使用 紅黑樹 結構存儲數據
-
TreeSet 兩種排序方法: 自然排序 和 定製排序 。默認情況下, TreeSet 採用自然排序。
自然排序:TreeSet 會調用集合元素的compare To(Object obj)方法來比較元素之間的大小關係,然後將集合元素按升序(默認情況)排列
定製排序:通過 Comparator 接口來實現。 需要重寫 compare(T o1,T o2) 方法
例子:
@Test
public void test2(){
TreeSet treeSet = new TreeSet();
treeSet.add(1);
treeSet.add(3);
treeSet.add(5);
treeSet.add(2);
treeSet.add(4);
treeSet.add(6);
Object first = treeSet.first();
Object last = treeSet.last();
Object o = treeSet.higher(5);
System.out.println(treeSet);
System.out.println(first);
System.out.println(last);
System.out.println(o);
}
結果:
如果有錯誤,請指出 😄
喜歡的話,就點個贊吧!!!