集合框架(List和Set)
一、概述
集合是一種可變數據項的容器,具有統一的父類接口Collection<E>
(Map
並沒有繼承之),與其子集合的關系例如以下 圖。集合的特點是長度可變,能夠存儲多種類型的對象(不加泛型時)。這也是與數組的兩點最大的不同。
java集合類關系圖 |
Collection
最為根接口,List
、Set
、Queue
接口均直接繼承自它,Map
接口盡管不是直接繼承自Collection
,可是接口中使用到了Collection
,即Map的數據也是使用Collection
研究集合不可缺少的是先研究
Collection
接口的內容,先從接口名字定義開始說起public interface Collection<E> extends Iterable<E>
,這是集合的根接口。<E>
是JDK1.5開始的泛型,其功能是提高安全性,增強針對性,詳細介紹後面再說。Iterable<E>
接口是叠代器。以下有詳細介紹。Collection
接口提供了一些集合的共性方法,如:
boolean add(E e)
加入一條數據。boolean addAll(Collection<?
加入還有一個集合內的元素。extends E> c)
void clear()
清除全部數據。boolean contains(Object o)
是否包括指定數據。Iterator<E> iterator()
返回叠代器。boolean remove(Object o)
刪除指定數據。<T>T[] toArray(T[] a)
返回數據的數組形式等等。boolean retainAll(Collection<?> c)
取與集合c的交集。boolean removeAll(Collection<?> c)
取與集合c的差集。
二、叠代器
一種集合的取出方式,用於集合的數據的遍歷等。
不同容器的數據結構不同,所以取出的動作實現不同,詳細的取出動作定義在每一個容器的內部,這樣取出方式就能夠直接訪問集合內部元素。取出方式就被定義成了內部類。可是都有共性內容推斷和取出,共性抽取便形成了Iterator
叠代器的使用演示樣例(暫時不考慮泛型):
ArrayList alist = new ArrayList();
alist.add("java01");
alist.add("java02");
alist.add("java03");
alist.add("java04");
for(Iterator it = alist.iterator(); it.hasNext(); ) {
System.out.println(it.next());
}
三、List
List
為Collection的常見子接口之中的一個,元素有序。能夠反復,有索引。
List
有些特有的方法,和下標相關的一些方法。如:
- 增:add(index, element)
指定位置加入元素,addAll(index, Collection)
指定位置加入一個集合的元素。
- 刪:remove(index)
刪除指定為的元素。
- 改:set(index, element)
改動指定位置的元素。
- 查:get(index)
獲取指定位置的元素。subList(from, to)
獲取[from, to)
段的子List
,listIterator()
獲取List
特有的叠代器,indexOf(E)
獲取對象的位置。
ListIterator
ListIterator
是Iterator
的子接口,在叠代時不能夠通過集合的操作對集合進行操作,會發生並發改動異常,可是Iterator
的操作是有限的,假設想對元素進行加入和改動等,就須要用到其子接口ListIterator
,該接口僅僅能通過List
的listIterator()
方法獲取,演示樣例代碼例如以下:
ArrayList alist = new ArrayList();
alist.add("java01");
alist.add("java02");
alist.add("java03");
alist.add("java04");
for(ListIterator it = alist.listIterator(); it.hasNext(); ) {
System.out.println(it.next());
it.add("java");
}
System.out.println(alist);
//輸出結果是
java01
java02
java03
java04
[java01, java, java02, java, java03, java, java04, java]
通過上面能夠看到,ListIterator
在獲取之後其遍歷的內容就已經確定下來了。 ListIterator
還有逆向遍歷功能。
ArrayList alist = new ArrayList();
alist.add("java01");
alist.add("java02");
alist.add("java03");
alist.add("java04");
ListIterator it = alist.listIterator();
while(it.hasNext()) {
System.out.println(it.next());
}
while(it.hasPrevious()) {
System.out.println(it.previous());
}
// 輸出的結果是
java01
java02
java03
java04
java04
java03
java02
java01
List
中詳細對象特點
List
有三個詳細的子類,各自是ArrayList
、LinkedList
和Vector
。
三者的特點與不同例如以下:
ArrayList
:底層數據結構為數組結構,線程非同步。查詢速度快,增刪速度慢,效率比Vector
高,最早出如今JDK1.2版本號。初始長度默覺得10。空間不足時50%延長。LinkedList
:底層數據結構為鏈表結構,線程非同步,查詢速度慢,增刪速度快,最早出如今JDK1.2版本號。Vector
:底層數據結構是數組結構,線程同步,與ArrayList
有同樣的功能。已被ArrayList
取代,最早出如今JDK1.0版本號。默認長度為10,空間不足時100%延長。
Vector
的特點
Vector
的特有的一點就是,Vector
的枚舉(Vector
的特有取出方式),和叠代器相似,名為Enumeration
,ArrayList
中沒有此功能。可是由於方法名過長,且沒有移除功能,已被Iterator
取代,所以一般優先考慮使用Iterator
。演示樣例代碼例如以下:
Vector v = new Vector();
v.add("java01");
v.add("java02");
v.add("java03");
Enumeration en = v.elements();
while(en.hasMoreElements()) {
System.out.println(en.nextElement());
}
// 輸出結果為
java01
java02
java03
LinkedList
LinkedList
的特有方法:addFirst(E e)
、addLast(E e)
加入到頭部或者尾部、getFirst()
、getLast()
獲取頭部元素或者尾部元素、removeFirst()
、removeLast()
刪除頭部元素或者尾部元素,並返回刪除的元素,沒有會報NoSuchElementException
,這些是LinkedList
所特有的方法。JDK1.6版本號名稱發生了變化,分別變成了offerFirst(E e)
,offerLast(E e)
,peekFirst()
,peekLast()
。pollFirst()
。pollLast()
。沒有會返回null
。
LinkedList
練習,模擬隊列進行加入和取出動作。演示樣例代碼例如以下:
import java.util.*;
class MyQueue {
private LinkedList link;
MyQueue() {
link = new LinkedList();
}
public void myAdd(Object obj) {
link.addFirst(obj);
}
public Object myGet() {
return link.removeFirst();
}
public boolean isNull() {
return link.isEmpty();
}
}
class LinkedListTest {
public static void main(String[] args) {
MyQueue q = new MyQueue();
q.myAdd("java01");
q.myAdd("java02");
q.myAdd("java03");
q.myAdd("java04");
while(!q.isNull()) {
System.out.println(q.myGet());
}
}
}
ArrayList
ArrayList
練習,去除ArrayList
中的反復項。
代碼示比例如以下:
import java.util.*;
class ArrayListTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("java01");
list.add("java02");
list.add("java01");
list.add("java02");
list.add("java01");
list.add("java03");
// 去除反復前
System.out.println(list);
list = singleElement(list);
// 去除反復後
System.out.println(list);
}
public static ArrayList singleElement(ArrayList list) {
//定義一個暫時容器。
ArrayList newList = new ArrayList();
// 獲取叠代器
Iterator it = list.iterator();
while(it.hasNext()) {
Object obj = it.next();
if(!newList.contains(obj)) {
newList.add(obj);
}
}
return newList;
}
}
ArrayList
練習。自己定義類加入至ArrayList
,須要覆蓋自己定義類的equals
方法。
import java.util.*;
class Person {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
// 覆蓋equals方法
public boolean equals(Object obj) {
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String toString() {
return "Name:" + this.name + " Age:" + this.age;
}
}
class ArrayListTest {
public static void main(String[] args) {
ArrayList list= new ArrayList();
list.add(new Person("lisi01",30));
list.add(new Person("lisi02",32));
list.add(new Person("lisi02",32));
list.add(new Person("lisi04",35));
list.add(new Person("lisi03",33));
list.add(new Person("lisi04",35));
System.out.println(list);
list = singleElement(list);
System.out.println(list);
}
public static ArrayList singleElement(ArrayList al) {
// 定義一個暫時容器。
ArrayList newList = new ArrayList();
// 取出叠代器
Iterator it = al.iterator();
while(it.hasNext()) {
Object obj = it.next();
if(!newList.contains(obj))
newList.add(obj);
}
return newList;
}
}
四、Set
元素無序(存入與取出的順序不一致),元素不可反復。
有兩個主要子類:HashSet
和TreeSet
。
HashSet
:底層數據結構為哈希表。TreeSet
:底層數據結構為二叉樹。
HashSet
依據hashCode()
和equals()
方法進行存儲。add
時。假設是第一次加入返回true
(加入成功),否則返回false
(加入失敗)。演示樣例代碼例如以下:
import java.util.*;
class HashSetDemo {
public static void main(String[] args) {
HashSet hs = new HashSet();
// 第一次加入java01返回true
System.out.println(hs.add("java01"));
// 第二次加入java01返回false
System.out.println(hs.add("java01"));
hs.add("java02");
hs.add("java03");
hs.add("java03");
hs.add("java04");
// 遍歷集合
Iterator it = hs.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
HashSet
存儲自己定義對象,自己定義對象須要覆蓋hashCode
和equals
方法。當hashCode
值同樣、equals
返回真時,覺得是同一個對象。加入時。當hashCode
值不同一時候,不會調用equals
比較,當hashCode
的值同樣時,才會調用equals
方法比較兩個對象。
演示樣例代碼例如以下:
import java.util.*;
/**
*姓名和年齡同樣覺得是同一個人
*/
class Person {
private String name;
private int age;
Person(String name,int age) {
this.name = name;
this.age = age;
}
public int hashCode() {
System.out.println(this.name+"....hashCode");
return name.hashCode() + age*37;
}
public boolean equals(Object obj) {
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
// 加入一個同樣的人
hs.add(new Person("a2",12));
hs.add(new Person("a4",14));
// 遍歷集合中的人
Iterator it = hs.iterator();
while(it.hasNext()) {
Person p = (Person)it.next();
System.out.println(p.getName()+"::"+p.getAge());
}
}
}
// 執行結果為
a1....hashCode
a2....hashCode
a3....hashCode
a2....hashCode
a2...equals..a2
a4....hashCode
a3::13
a1::11
a4::14
a2::12
從上面代碼執行的結果能夠看出,當加入new Person("a2",12)
時。由於集合中已經有這個人了。所以hashCode
的返回值已經存在。因此便會調用equals方法推斷,終於結果還是此人已存在,所以最總集合中僅僅有4個人。
HashSet的推斷和刪除依據,能夠看到判讀元素是否存在和刪除元素都是首先依據hashCode
值,然後再通過equals
推斷是否同樣。
import java.util.*;
/**
*姓名和年齡同樣覺得是同一個人
*/
class Person {
private String name;
private int age;
Person(String name,int age) {
this.name = name;
this.age = age;
}
/**
*統一返回1作為哈希值
*/
public int hashCode() {
System.out.println("hashCode");
return 1;
}
/**
* 姓名和年齡同樣覺得是同一個人
*/
public boolean equals(Object obj) {
if(!(obj instanceof Person))
return false;
Person p = (Person)obj;
System.out.println(this.name+"...equals.."+p.name);
return this.name.equals(p.name) && this.age == p.age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class HashSetTest {
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add(new Person("a1",11));
hs.add(new Person("a2",12));
hs.add(new Person("a3",13));
// 加入一個同樣的人
hs.add(new Person("a2",12));
hs.add(new Person("a4",14));
System.out.println("--------");
// 推斷是否存在元素
hs.contains(new Person("a4", 14));
System.out.println("--------");
// 刪除指定元素
hs.remove(new Person("a1", 11));
}
}
// 執行結果為
hashCode
hashCode
a2...equals..a1
hashCode
a3...equals..a1
a3...equals..a2
hashCode
a2...equals..a1
a2...equals..a2
hashCode
a4...equals..a1
a4...equals..a2
a4...equals..a3
--------
hashCode
a4...equals..a1
a4...equals..a2
a4...equals..a3
a4...equals..a4
--------
hashCode
a1...equals..a1
TreeSet
能夠對集合中的元素進行排序。
底層數據結構為二叉樹。中序遍歷便是TreeSet
的順序。
演示樣例代碼例如以下:
import java.util.*;
class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add("asdas");
ts.add("abc");
ts.add("beds");
ts.add("abd");
Iterator it = ts.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
}
// 執行結果為
abc
abd
asdas
beds
對於自己定義對象的排序,一般有兩種做法,一是自己定義對象自己實現Comparable
接口。二是集合提供比較器(Comparator
接口)。
自己定義對象實現Comparable
接口,演示樣例代碼例如以下:
import java.util.*;
class Student implements Comparable {
private String name;
private int age;
Student(String name,int age) {
this.name = name;
this.age = age;
}
/**
*比較結果又三種。負數表示小於,0表示等於,正數表示大於
*/
public int compareTo(Object obj) {
if(!(obj instanceof Student))
throw new RuntimeException("不是學生對象");
Student s = (Student)obj;
System.out.println(this.name+"....compareto....."+s.name);
if(this.age>s.age)
return 1;
if(this.age==s.age) {
// String類已經實現了Comparable接口
return this.name.compareTo(s.name);
}
return -1;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class TreeSetDemo {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Student("lisi02",22));
ts.add(new Student("lisi007",20));
ts.add(new Student("lisi09",19));
ts.add(new Student("lisi08",19));
ts.add(new Student("lisi007",20));
ts.add(new Student("lisi01",40));
// 遍歷集合
Iterator it = ts.iterator();
while(it.hasNext()) {
Student stu = (Student)it.next();
System.out.println(stu.getName()+"..."+stu.getAge());
}
}
}
集合提供比較器(Comparator
接口)的比較方式。用於元素不具備比較性或者具備的比較性不是用戶所須要的時。演示樣例代碼例如以下:
import java.util.*;
class Student implements Comparable {
private String name;
private int age;
Student(String name,int age) {
this.name = name;
this.age = age;
}
/**
*比較結果又三種,負數表示小於,0表示等於,正數表示大於
*/
public int compareTo(Object obj) {
if(!(obj instanceof Student))
throw new RuntimeException("不是學生對象");
Student s = (Student)obj;
System.out.println(this.name+"....compareto....."+s.name);
if(this.age>s.age)
return 1;
if(this.age==s.age) {
return this.name.compareTo(s.name);
}
return -1;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
class TreeSetDemo2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyCompare());
ts.add(new Student("lisi02",22));
ts.add(new Student("lisi02",21));
ts.add(new Student("lisi007",20));
ts.add(new Student("lisi09",19));
ts.add(new Student("lisi06",18));
ts.add(new Student("lisi06",18));
ts.add(new Student("lisi007",29));
ts.add(new Student("lisi007",20));
ts.add(new Student("lisi01",40));
Iterator it = ts.iterator();
while(it.hasNext()) {
Student stu = (Student)it.next();
System.out.println(stu.getName()+"..."+stu.getAge());
}
}
}
class MyCompare implements Comparator {
public int compare(Object o1,Object o2) {
Student s1 = (Student)o1;
Student s2 = (Student)o2;
// 姓名比較優先
int num = s1.getName().compareTo(s2.getName());
if(num==0) {
// 姓名同樣時,年齡小的在前
return new Integer(s1.getAge()).compareTo(new Integer(s2.getAge()));
}
return num;
}
}
五、泛型
泛型的出現攻克了類型不同的安全問題,JDK1.5版本號開始出現的。詳細做法就是在集合定義時指明數據類型。如ArrayList<String> list= new ArrayList<String>()
,這樣一來假設向list
中加入非String
類型對象時。就會在編譯時期提示ClassCastException
異常。演示樣例代碼例如以下:
import java.util.*;
class GenericDemo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("abc01");
list.add("abc0991");
list.add("abc014");
// Iterator也有相應的泛型
Iterator<String> it = al.iterator();
while(it.hasNext()) {
System.out.println(s+":"+s.length());
}
}
}
自己定義類中使用泛型的概念。試用於用數據類型不確定時,演示樣例代碼例如以下:
class Worker {
}
class Student {
}
/**
*泛型類,使用與引用數據類型不確定時。
*/
class Utils<Q> {
private Q q;
public void setObject(Q q) {
this.q = q;
}
public Q getObject() {
return q;
}
}
class GenericDemo3 {
public static void main(String[] args) {
Utils<Worker> u = new Utils<Worker>();
u.setObject(new Worker());
Worker w = u.getObject();;
}
}
泛型定義在方法上,演示樣例代碼例如以下:
class Demo {
public <T> void show(T t) {
System.out.println("show:"+t);
}
public <Q> void print(Q q) {
System.out.println("print:"+q);
}
/**
* 靜態方法不能夠訪問類上的泛型。可使用靜態泛型方法。註意:泛型標識<E>需放在返回值前。
*/
public static <E> void printf(E e) {
System.out.println("printf:"+e);
}
}
class GenericDemo4 {
public static void main(String[] args) {
Demo demo = new Demo();
demo.show("Hi");
demo.print("Hello");
Demo.printf(5);
}
}
//執行結果為
show:Hi
print:Hello
show:5
泛型限定
一般有三種情況。
<?
:?為通配符。>
<? extends E>
:E和E的子類。稱為上限。<? super E>
:E和E的父類。稱為下限。
如對全部類型的ArrayList
進行遍歷輸出,演示樣例代碼例如以下:
public static void print(ArrayList<?> list) {
Iterator<?> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next().toString());
}
}
對Person
或者Person
的子類Student
進行遍歷並輸出,演示樣例代碼例如以下:
public static void print(ArrayList<? extends Person> list) {
Iterator<? extends Person> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next().getName());
}
}
對於<?
super E>並不經常使用。舉一個TreeSet裏的樣例,TreeSet(Comparator<?
super E> comparator),使用父類的比較器進行子類的比較。演示樣例代碼例如以下:
import java.util.*;
class Person {
private String name;
Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public String toString() {
return "Person :"+name;
}
}
class Student extends Person {
Student(String name) {
super(name);
}
}
class Worker extends Person {
Worker(String name) {
super(name);
}
}
/**
* 定義使用與Person的比較器
*/
class Comp implements Comparator<Person> {
public int compare(Person p1,Person p2) {
// 倒序
return p2.getName().compareTo(p1.getName());
}
}
class GenericDemo {
public static void main(String[] args) {
// 使用使用父類的比較器進行比較
TreeSet<Student> ts = new TreeSet<Student>(new Comp());
ts.add(new Student("abc03"));
ts.add(new Student("abc02"));
ts.add(new Student("abc06"));
ts.add(new Student("abc01"));
Iterator<Student> it = ts.iterator();
while(it.hasNext()) {
System.out.println(it.next().getName());
}
// 使用使用父類的比較器進行比較
TreeSet<Worker> ts1 = new TreeSet<Worker>(new Comp());
ts1.add(new Worker("wabc--03"));
ts1.add(new Worker("wabc--02"));
ts1.add(new Worker("wabc--06"));
ts1.add(new Worker("wabc--01"));
Iterator<Worker> it1 = ts1.iterator();
while(it1.hasNext()) {
System.out.println(it1.next().getName());
}
}
}
// 執行結果為
abc06
abc03
abc02
abc01
wabc--06
wabc--03
wabc--02
wabc--01
集合框架(List和Set)