Collection體系
根據jdk介紹它是整個集合框架的根節點
實現了它的接口可以存放一組任意多個、任意類型的數據。衆所周知的List和Set都可以看作是Collection,它們有的允許元素重複(List),有的不允許(Set)
下面就用一張圖來看看它們之間的具體關係:
Collection體系圖
List
List,有序集合(也稱爲序列)。實現了Collection,所以可以說它就是一個Collection,它具有了Collection的所有非私有特性,並且在此基礎之上拓展出了一些屬於自己的
特有屬性及功能。這個接口的用戶對列表中的每個元素都有精確的控制。用戶可以通過其整數索引(列表中的位置)訪問元素,並在列表中搜索元素
ArrayList
底層實現是基於數組的,實現所有可選列表操作,並允許所有元素,包括null。除了實現列表接口之外,這個類還提供了方法來操作在內部使用的數組的大小來存儲列表。(這個類大
致相當於向量,但它是不同步的。)可以通過List list = Collections.synchronizedList(new ArrayList(...));獲得同步的ArrayList集合。ArrayList是可以自動擴容的,
但是它的實現原理也無非就是重新創建一個容器再將現有元素賦值到新容器中,再把變量指向修改。
用一些代碼來看看它具體提供的一些方法:
import java.util.ArrayList;
import java.util.Collection;
/**
* ArrayList
*
* @author xer
*
*/
public class ArrayListTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("XX");
list.add(true);
list.add("XX");
list.add("OO");
list.add(100);
list.add("XX");
list.add("xx");
list.add("tt");
list.add("ss");
System.out.println( list.size());
/**
* add(int index, E element)
* Inserts the specified element at the specified position in this list.
* 在指定位置插入元素
*/
list.add(2,"next");
System.out.println(list.size());
System.out.println(list);
/**
* addAll(Collection<? extends E> c)
* Appends all of the elements in the specified collection to the end of this list,
* in the order that they are returned by the specified collection's Iterator.
* 在列表尾部插入一個集合
*/
Collection col = new ArrayList();
col.add("xx");
list.addAll(col);
System.out.println(list.size());
System.out.println(list);
/**
* contains(Object o)
* Returns true if this list contains the specified element.
* 如果列表中有此元素則返回true
*/
System.out.println(list.contains("xx"));
/**
* get(int index)
* Returns the element at the specified position in this list.
* 返回指定下標處的元素
*/
System.out.println(list.get(3));
/**
* indexOf(Object o)
* Returns the index of the first occurrence of the specified element in this list,
* or -1 if this list does not contain the element.
* 返回指定元素在列表中第一次出現的位置,如果爲找到則返回-1
*/
System.out.println(list.indexOf("XX"));
/**
* remove(int index)
* Removes the element at the specified position in this list.
* 刪除指定下標處元素
*/
list.remove(2);
System.out.println(list.size());
System.out.println(list);
/**
* set(int index, E element)
* Replaces the element at the specified position in this list with the specified element.
* 替換指定下標處的元素爲傳入的指定元素
*/
list.set(2, "瘦狗");
System.out.println(list.size());
System.out.println(list);
}
}
在ArrayList中遍歷元素的方法
- for循環遍歷
- foreach遍歷
- Iterator迭代器遍歷
- ListIterator雙向遍歷(提供正向遍歷與反向遍歷)
for遍歷
System.out.print("for:");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i)+", ");
}
foreach遍歷(其實它的底層實現就是迭代器)
for (Object object : list) {
System.out.print(object+", ");
}
Iterator迭代器遍歷
/**
*boolean hasNext():判斷是否還有下一個元素
*E next():返回這個迭代器中的下一個元素
*/
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+", ");
}
ListIterator雙向遍歷
/**
*boolean hasPrevious():返回true,如果上一個元素不爲空
*E previous():返回這個迭代器中的上一個元素
*/
System.out.print("listIterator-next:");
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()) {
System.out.print(listIterator.next()+", ");
}
System.out.println();
System.out.print("listIterator-pervious:");
while(listIterator.hasPrevious()) {
System.out.print(listIterator.previous()+", ");
}
LinkedList
底層實現是基於鏈表的,實現所有可選列表操作,並允許所有元素(包括null)。由於它也實現了 Deque<E>, Queue<E> 接口,所以也可以充當隊列以及堆棧結構使用。
LinkedList也是支持索引的,不過相較於ArrayList,LinkedList的優勢在新增、刪除方面更大一些。同樣LinkedList也是線程不安全的,可以通過
List list = Collections.synchronizedList(new LinkedList(...));獲得一個同步的LinkedList集合
LinkedList的基本方法與遍歷操作等於ArrayList大同小異,這裏就不再過多贅述
Set
Set也是可以存放任意多個、任意類型的數據,但是是無序的(存放順序與迭代順序)並且不允許重複
HashSet
底層實現基於數組,不允許重複,這個類實現了集合接口,由哈希表(實際上是HashMap實例)支持。它對集合的迭代順序沒有任何保證;這個類允許空元素
如何判斷元素重複的
- equals
hashCode
實際開發中我們通常放入HashSet中的元素是屬於自定義類型的,但是我們的類型並不能被正確的判斷是否重複,因爲java定義好的類型都是重寫了equals和hashCode方法的。所以如果我們想要正確存入自定義類型就必須重寫這兩個方法
import java.util.HashSet; import java.util.Random; public class HashSetTest { public static void main(String[] args) { HashSet hashSet = new HashSet(); hashSet.add(10); hashSet.add(10); hashSet.add(10); hashSet.add("10"); hashSet.add("10"); Student stu = new Student("tom", 22); Student stu1 = new Student("tom", 22); hashSet.add(stu1); hashSet.add(stu); System.out.println(hashSet.size()); System.out.println(hashSet); //在HashSet中不同元素類型也會被看成不同的元素 } } class Student { String name; int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { // TODO Auto-generated method stub return "name:"+name+" age:"+age; } //IDE自動生成的hashCode以及equals方法 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + age; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Student other = (Student) obj; if (age != other.age) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }
TreeSet
TreeSet底層實現是基於紅黑樹的,也就是二叉樹。它是無序的,不允許重複,在它的內部元素按照自然排序排列,如果你自定義的類沒有實現Comparable接口,是直接會添加
失敗的。當然,你也可以選擇自定義一個定製排序器:自定義排序器類實現Comparator接口,覆寫compare方法,再使用TreeSet的有參構造方法把排序器移植到TreeSet中成
爲TreeSet內部的“裁判”,那麼它也是可以實現自己排序的。有一點需要注意:理論上TreeSet是可以存放任意類型的數據,但是一旦它內部存放了一種類型數據後就不能再
存放其他類型的數據了。這一點想想就能明白了,TreeSet它存放數據是要對它們進行排序的,如果兩個對象都不是一個類型的,那要怎麼比較呢?
下面我們就一起用代碼來看看具體實現,
首先,我們看看TreeSet自己內部的自然排序是怎樣的
import java.util.TreeSet;
/**
* TreeSet中可以存放任意類型的元素,並且都是有序的(自然排序)
* 一旦放入了一種類型的元素之後就不能放入其他類型的元素
*
*
* 放入自定義類型
* 放入的自定義類型必須是實現了Comparable接口的
* @author xer
*
*/
public class TreeSetTest {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
Student student2 = new Student("Tom",12);
Student student = new Student("Tom",11);
Student student1 = new Student("Tom",11);
treeSet.add(student2);
treeSet.add(student1);
treeSet.add(student);
System.out.println(treeSet.size());
System.out.println(treeSet);
}
}
class Student implements Comparable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//如果覆寫compareTo方法,那麼它默認返回0,說明兩個元素是相等的(重複)
/**
* compareTo方法會返回一個整數,
* 負整數、0、整數
* 負整數說明此元素小於傳入元素
* 0說明兩元素相等
* 正整數說明此元素大於傳入元素
*/
@Override
public int compareTo(Object o) {
Student stu = (Student)o;
if(this.age > stu.age) {
return 1;
}else if(this.age < stu.age) {
return -1;
}else {
return this.name.compareTo(stu.name);
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
結果:
明顯可以看出兩個元素是同一個元素,所以只會裝入一個,但是不同的兩個元素裝入之後是有序的
現在,在現實生活中,有許多時候我們都需要用到其他人寫好的類,可能他們本身就已經寫好了排序的規則(覆寫好了comparTo方法)但是我們想要不同的排序規則,這時我們又不能去修改他們的類,要怎麼辦呢?
這時我們就能用到定製排序了
定製排序:自定義一個定製排序器類實現Comparator接口並且覆寫compare方法
下面是具體代碼實現:
StudentComparator.java (自定義的排序器類)
import java.util.Comparator;
/**
* 自定義比較器
* @author xer
*
*/
public class StudentComparator implements Comparator{
/**
* 根據傳入的兩個元素大小比價,返回大於:整數,等於:0,小於:負數
*/
@Override
public int compare(Object o1, Object o2) {
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
if(stu1.age > stu2.age) {
return 1;
}else if(stu1.age < stu2.age) {
return -1;
}else {
return stu1.name.compareTo(stu2.name);
}
}
}
TreeSetTest.java
import java.util.TreeSet;
/**
*
*
* 自定義比較器
* 當自定義的類具有自己的自然排序能力時,再給TreeSet設置一個自定義比較器
* 會採用自定義比較器的比較規則
* @author xer
*
*/
public class TreeSetTest {
public static void main(String[] args) {
StudentComparator sc = new StudentComparator();
TreeSet treeSet = new TreeSet(sc); //將自定義的比較器傳入,成爲“裁判”
Student student = new Student("Tom",12);
Student student1 = new Student("Tom",11);
treeSet.add(student1);
treeSet.add(student);
System.out.println(treeSet.size());
System.out.println(treeSet);
}
}
class Student implements Comparable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//如果覆寫compareTo方法,那麼它默認返回0,說明兩個元素是相等的(重複)
/**
* compareTo方法會返回一個整數,
* 負整數、0、整數
* 負整數說明此元素小於傳入元素
* 0說明兩元素相等
* 正整數說明此元素大於傳入元素
*/
@Override
public int compareTo(Object o) {
Student stu = (Student)o;
if(this.age > stu.age) {
return -1;
}else if(this.age < stu.age) {
return 1;
}else {
return this.name.compareTo(stu.name);
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
結果:
可以看到,無論Student類是否實現Comparable接口並且定義重寫了compareTo方法,TreeSet內部都會按照我們的自定義排序器內的規則進行排序,這樣就實現了既不用改別人的代碼也可以實現自己的想法的願望了。這也充分的體現了面向對象的思想。