學java其實學了很久了,但其實一直對容器沒有一個深入的瞭解。瞭解的東西其實都是最簡單的、常用的一些方法。
在這裏我將對java的容器做一個系統的,比較淺顯的回顧。包括容器的類型、容器的底層實現原理、具體的實例。
一、容器的原理與底層
容器:Collection收集,集合。本身也是一個對象,只不過它可以容納其他的對象。前面比較典型的容器是數組Array[]
在這裏我們主要要關注以下幾個接口:Collection(容器)、Map(鍵值對)
Collection接口:定義了存取一組對象的方法,子接口Set、List、接口
Set:無序不可重複,若重複則後面的值會把前面的覆蓋掉,子類Hashset
List:有序可重複
Map接口:(注意Map不是繼承Collection的)單獨的接口,鍵值對,子類有HashMap、TreeMap,允許值重複,鍵不能重複
Collection接口:size、isEmpty、contains、iterator、toArray、add、remove、containsAll(是否包含另一個集合裏的所有對象)、addAll、equals、hashCode
1、List的底層實現:數組實現,它的特點是每個元素都有索引,因此優點也顯而易見就是查詢速度快。但是修改刪除插入效率低。由於刪除和插入會引起元素的移動,因此效率低下。
自定義ArrayList,實現list 的獲取、增加、刪除元素。Arraylist由於是數組實現,因此在擴容的時候採取的是將容量擴大到兩倍。
import java.util.ArrayList;
import java.util.Date;
public class MyArrayList {
private Object[] element;
private int size;
public MyArrayList(){
this(10);
}
public MyArrayList(int capacity){
if(capacity<0){
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
element = new Object[capacity];
}
public void add(Object e){
// 數組擴容
if(size == element.length){
Object[] newArr = new Object[size*2+1];
System.arraycopy(element,0 ,newArr,0,element.length);
element = newArr;
}
element[size++] = e;
}
public Object get(int index){
changeIndex(index);
return element[index];
}
public void remove(int index){
if(index <0 || index >=size){
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
//需要移動元素 ,未寫完
}
public void changeIndex(int index){
if(index<0||index>=size){
try {
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyArrayList ml=new MyArrayList();
ArrayList l = new ArrayList();
ml.add("1");
ml.add(new Date());
System.out.println(ml.get(1));
}
}
2、LinkedList原理+自定義實現
採用雙向鏈表實現,他的特點是採用鏈表的原理,因此查詢較List更慢,因爲它沒有索引,因此必須遍歷整個鏈表,插入修改刪除效率較高,無需移動元素。
自定義實現add、remove、get方法
public class MyLinkedList { private Node first; private Node last; private int size; public MyLinkedList() { } public void add(Object e){ if(null == first){ Node n = new Node(); n.setPre(null); n.setObj(e); n.setNext(null); first = n; last = n; }else{ Node n = new Node(); n.setPre(last); n.setObj(e); n.setNext(null); last.setNext(n); last = n; } size++; } public int size(){ return size; } public Object get(int index){ Node current =null; if(null==first ){ }else{ changeIndex(index); current = first; for(int i =0;i<index;i++){ current = current.next; } return current.obj; } return current; } public void remove(int index){ changeIndex(index); Node current = node(index); if(current!=null){ Node up = current.pre; Node down = current.next; up.next = down; down.pre = up; size --; } } public void changeIndex(int index){ if(index<0||index>=size){ try { throw new Exception(); } catch (Exception e) { e.printStackTrace(); } } } public Node node(int index){ Node current =null; changeIndex(index); current = first; for(int i =0;i<index;i++){ current = current.next; } return current; } public static void main(String[] args) { MyLinkedList ml= new MyLinkedList(); ml.add(3); ml.add(4); ml.add(5); // System.out.println(ml.get(1)); ml.remove(2); System.out.println(ml.size); } }
public class Node { Node pre; Object obj; Node next; public Node(){ } public Node(Node pre, Object obj, Node next) { this.pre = pre; this.obj = obj; this.next = next; } public Object getPre() { return pre; } public void setPre(Node pre) { this.pre = pre; } public void setNext(Node next) { this.next = next; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public Object getNext() { return next; } }3、Vector:
Vector:線程安全的,效率低。前兩者線程不安全,效率高
我們需要掌握手寫ArrayList的add、get、remove方法。
4、Hashmap
Hashmap在JDK1.7以前是採用數組+鏈表的形式實現的,數組存儲hashcode的地址,使用鏈表處理衝突,同一hash值的鏈表都存儲在一個鏈表裏。但是當位於一個數組中的元素較多,即hash值相等的元素較多時,通過key值依次查找的效率較低。
JDK1.8中,HashMap採用位桶+鏈表+紅黑樹實現,當鏈表長度超過閾值(8)時,將鏈表轉換爲紅黑樹,這樣大大減少了查找時間。
簡單說一下原理,當我們在調用hashmap的add方法,我們需要先計算插入的key的hash值,來判斷它屬於數組的哪一個位置,比如計算出來的hash值是1,那麼就存在arr[1]這個位置,但是hash值可能會重複,那麼重複的這個元素也會被放在arr[1]中,這時候建立鏈表,將同一hash值的元素放入鏈表中,但當鏈表長度過大時,效率又降低了,因此當它的長度超過某一個閾值的時候,將鏈表轉換爲紅黑樹提高效率。
圖如下:
兩個內容相同的對象的hashcode一定相等,兩個key相等,則hash碼一定相等,反之不然。hashcode重寫,則equals也必須重寫。保證兩個內容相同的對象是一樣的。hashmap裏面的鍵不能重複的原理是覆蓋,如果有重複則覆蓋掉了
自定義Map實現get和put(初級版本):
import java.util.HashMap;
import java.util.LinkedList;
/**
* 自定義map的升級版
* 提高查詢的效率
*/
public class Mymap01 {
// myEntry[] arr = new myEntry[999];
LinkedList[] arr = new LinkedList[999];//map的底層機構 數組+鏈表
int size;
public void put(Object key,Object value)
{
myEntry m = new myEntry(key,value);
int a = key.hashCode()%999;
if (null == arr[a]){
LinkedList list = new LinkedList();
arr[a] = list;
list.add(m);
}else{
//需要做判斷,避免key重複
LinkedList list= arr[a];
for(int i =0;i<list.size();i++){
myEntry e=(myEntry) list.get(i);
if(e.key.equals(key)){
e.value = value;
return;
}
}
arr[a].add(m);
}
}
public Object get(Object key){
int a = key.hashCode()%arr.length;
if (null != arr[a]){
LinkedList list = arr[a];
for(int i =0;i<list.size();i++){
myEntry e=(myEntry) list.get(i);
if(e.key.equals(key)){
return e.value;
}
}
}
return null;
}
public static void main(String[] args) {
Mymap01 map = new Mymap01();
map.put("我","白");
map.put("你","黑");
System.out.println(map.get("我"));
map.put("name",2);
HashMap map1 = new HashMap();
}
}
5、Set
底層是通過map實現的。無序集合,沒有重複元素,這是利用map的鍵不重複的原理,重複則覆蓋
/** * @author lively * 自己定義hashSet,不能有重複元素 */ public class MySet { HashMap map; int size; private static final Object PRESENT = new Object(); public int size(){ return map.size(); } public MySet(){ map = new HashMap(); } public void add(Object obj){ map.put(obj,PRESENT);//Set的不可重複就是利用了hashMap裏面的鍵不可重複 size++; } public void remove(Object obj){ } public static void main(String[] args) { // Set s = new HashSet(); // s.add("1"); // s.add("2"); // s.add("1"); // Iterator it = s.iterator(); // System.out.println(s.size()); // while(it.hasNext()){ // System.out.println(it.next()); // } MySet mySet = new MySet(); mySet.add("name"); mySet.add("name"); mySet.add(new String("name")); mySet.add(new String("name")); System.out.println(mySet.size()); } }
6、map裏面最重要的一個方法:Iterator
Iterator迭代器遍歷Map和Set,所有實現了Collection接口的容器類都有一個iterator方法以返回一個實現Iterator接口的對象。
使用迭代器遍歷List和map的兩個方法:for和while(後者用的居多)
三個方法:boolean hasNext,next方法,remove方法 自己實現
/** * 加入接口,提供方法 */ public class MyIterator2 { String[] elem = {"a","b","c","d","e","f","g"}; int size = elem.length;//簡化迭代器原理 private int cursor=-1; public Iterator<String> iterator() { return new Iterator<String>() { @Override public boolean hasNext() { { return (cursor +1!=size); } } @Override public String next() { cursor++; return elem[cursor]; } public void remore(String e){ } }; } public static void main(String[] args) { MyIterator2 m = new MyIterator2(); Iterator iterator = m.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } } }
容器的原理和使用就說到這裏,重點要掌握容器的底層原理,自定義map、list的常用方法等。
未完待續。。。