順序表
順序表是用一段物理地址連續的存儲單元依次存儲數據元素的線性結構,一般情況下采用數組存儲。在數組上完成數據的增刪查改。在物理和邏輯上都是連續的。
模擬實現
下面是我們要自己模擬實現的方法:
首先我們要創建一個順序表,順序表包含一個數組,一個由于計數的計數器,還有一個默認的初始容量,我這里定義初始容量為5,比較好判斷擴容是否成功。這里以整型數組為例:
private int[] array;private int usedSize;//目前數據總數private static final int DEFAULT_CAPACITY = 5;//默認容量
默認構造方法
這里我們直接在構造方法里給數組進行初始化:
public MyArrayList() {array = new int[DEFAULT_CAPACITY];}
尾插
尾插是指直接在所有數據的后面插入新數據,這里我們要注意數組容量是否已滿,所以我們先寫一個isFull方法判斷數組是否容量已滿:
private boolean isFull() {if(usedSize == array.length) {return true;}return false;}
這個方法設置成private是因為這個方法只是給本類的方法提供的,不需要對外公開。
如果數組已滿,那么我們就需要擴容,這里我擴容2倍:
private void grow() {array = Arrays.copyOf(array,array.length * 2);}
現在來寫尾插代碼:
public void add(int data) {//判滿if(isFull()) {grow();}//插入數據array[usedSize++] = data;}
pos位置插入數據
首先我們要先檢查pos位置是否合法,如果不合法的話就不用進行插入操作,直接拋出異常,我們先寫異常代碼:
public class PosException extends RuntimeException{public PosException(String message) {super(message);}
}
檢查pos位置是否合法:
private void checkPosInAdd(int pos) throws PosException {if(pos < 0 || pos > usedSize) {throw new PosException("pos位置不合法");}}
現在寫插入代碼,首先判斷pos是否合法,然后判斷是否要擴容,最后我們進行插入操作即可,在插入代碼中,我們使用try-catch來處理異常。
public void add(int pos,int data) {try{checkPosInAdd(pos);//檢查位置是否合法if(isFull()) { //判滿grow();}//移動數據for (int i = usedSize; i >= pos ; i--) {array[i+1] = array[i];}//插入數據array[pos] = data;usedSize++;}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}}
contains
是否包含某個元素,使用布爾值進行返回,這里直接遍歷數組查找即可。
public boolean contains(int toFind) {for (int i = 0; i < usedSize; i++) {if(array[i] == toFind){return true;}}return false;}
indexOf
查找某個元素的下標,找到則返回該元素的下標,沒有找到則返回 -1
public int indexOf(int toFind) {for (int i = 0; i < usedSize; i++) {if(array[i] == toFind) {return i;}}return -1;}
get
找到pos位置的元素,這里要注意先判斷pos是否合法,但是這里的檢查pos和上面我們寫過的checkPosInAdd是不一樣的,這里的pos必須是有元素的下標范圍,也就是不包含usedSize這個下標,因為這個是沒有有效數據的,是待插入的空位,所以我們要再寫一個檢查pos方法:
private void checkPosInFind(int pos) throws PosException {if(pos < 0 || pos >= usedSize) {throw new PosException("pos位置不合法");}}
public int get(int pos) {try{checkPosInFind(pos);return array[pos];}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}return -1;}
set
更新pos位置的數據,還是要判斷pos位置是否合法,這里使用發判斷方法應該為checkPosInFind
public void set(int pos, int data) {try{checkPosInFind(pos);array[pos] = data;}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}}
remove
刪除第一次出現的關鍵字key
public void remove(int key) {for (int i = 0; i < usedSize; i++) {if(array[i] == key) {for (int j = i; j < usedSize - 1; j++) {array[j] = array[j+1];}usedSize--;return;}}}
size
獲取順序表的長度,這里直接返回usedSize即可
public int size() {return usedSize;}
display
打印順序表,該方法是便于測試,真正的順序表并沒有這個方法
public void display() {for (int i = 0; i < usedSize; i++) {System.out.print(array[i] + " ");}System.out.println();}
clear
清空順序表,直接將usedSize賦值為0即可,下次使用的時候,會直接覆蓋之前的數據的。
public void clear() {usedSize = 0;}
完整代碼
import java.util.Arrays;public class MyArrayList {private int[] array;private int usedSize;//目前數據總數private static final int DEFAULT_CAPACITY = 5;//默認容量public MyArrayList() {array = new int[DEFAULT_CAPACITY];}/*判滿,滿則返回true,否則返回false*/private boolean isFull() {if(usedSize == array.length) {return true;}return false;}//擴容 2倍擴容private void grow() {array = Arrays.copyOf(array,array.length * 2);}//尾插public void add(int data) {//判滿if(isFull()) {grow();}//插入數據array[usedSize++] = data;}//判斷pos是否合法/*不合法則拋出異常增加方法*/private void checkPosInAdd(int pos) throws PosException {if(pos < 0 || pos > usedSize) {throw new PosException("pos位置不合法");}}//指定pos位置插入數據public void add(int pos,int data) {try{checkPosInAdd(pos);//檢查位置是否合法if(isFull()) { //判滿grow();}//移動數據for (int i = usedSize; i >= pos ; i--) {array[i+1] = array[i];}//插入數據array[pos] = data;usedSize++;}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}}//是否包含某個元素public boolean contains(int toFind) {for (int i = 0; i < usedSize; i++) {if(array[i] == toFind){return true;}}return false;}//查找某個元素的下標public int indexOf(int toFind) {for (int i = 0; i < usedSize; i++) {if(array[i] == toFind) {return i;}}return -1;}//判斷pos是否合法/*查找方法不合法則拋出異常*/private void checkPosInFind(int pos) throws PosException {if(pos < 0 || pos >= usedSize) {throw new PosException("pos位置不合法");}}//獲取pos位置的元素public int get(int pos) {try{checkPosInFind(pos);return array[pos];}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}return -1;}//更新pos位置的數據public void set(int pos, int data) {try{checkPosInFind(pos);array[pos] = data;}catch (PosException e) {System.out.println("pos位置不合法!");e.printStackTrace();}}//刪除第一次出現的關鍵字keypublic void remove(int key) {for (int i = 0; i < usedSize; i++) {if(array[i] == key) {for (int j = i; j < usedSize - 1; j++) {array[j] = array[j+1];}usedSize--;return;}}}//獲取順序表的長度public int size() {return usedSize;}//打印順序表,該方法是便于測試,真正的順序表并沒有這個方法public void display() {for (int i = 0; i < usedSize; i++) {System.out.print(array[i] + " ");}System.out.println();}//清空順序表public void clear() {usedSize = 0;}
}
ArrayList 的使用
Java已經封裝好順序表供我們使用,就是ArrayList,現在我們來列舉其中的常用的方法。
方法 | 解釋 |
---|---|
boolean add(E e) | 尾插 e |
void add(int index, E element) | 將 e 插入到 index 位置 |
boolean addAll(Collection<? extends E> c) | 尾插 c 中的元素 |
E remove(int index) | 刪除index位置元素 |
boolean remove(Object o) | 刪除遇到的第一個o |
E get(int index) | 獲取下標 index 位置元素 |
E set(int index, E element) | 將下標 index 元素設置為 element |
void clear() | 清空 |
boolean contains(Object o) | 判斷 o 是否在線性表中 |
int indexOf(Object o) | 返回第一個 o 所在下標 |
int lastIndexOf(Object o) | 返回最后一個 o 的下標 |
更多詳細的方法可自行查閱官方文檔
上面很多方法是我們自己模擬實現過的,這里就不一一舉例演示。
ArrayList 的成員方法
ArrayList 構造方法
一共提供了三個構造方法:
方法 | 解釋 |
---|---|
ArrayList() | 無參構造 |
ArrayList(Collection<? extends E> c) | 利用其他 Colletion 構建 ArrayList |
ArrayList(int initialCapacity) | 指定順序表初始容量 |
ArrayList(int initialCapacity)指定順序表初始容量看一下源碼還是很好理解的,初始容量大于零就開辟一塊連續的空間,等于零就給一個空數組,小于零則拋出異常。
首先要知道下面的關系圖:
從上圖我們可以得知ArrayList是包含Collection這個接口的,所以可以接收Colletion的參數,Colletion后面的<? extends E> 中的 ? 是通配符,后面的文章會提到。
我們重點是看無參的構造方法:
直接賦值一個空數組,大家來看一下下面的代碼,明明是空數組,但是為什么可以直接add而不會報錯呢?
public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();list.add(10);list.add(20);System.out.println(list);}
我們點過去看看源碼是什么:
到了這里大家應該明白了,在使用add的時候,我們看到源碼里寫了當 s == 數組長度的時候,Java底層會幫我們自動擴容。
ArrayList 實例化
我們經常使用List或者ArrayList來接收順序表實例化的對象.
List<Integer> list = new ArrayList<>();ArrayList<Integer> list2 = new ArrayList<>();
由于List是ArrayList的接口,所以可以接收,但是注意List是接口意味著不能進行實例化,使用List接收的參數只能使用List有點方法,由于ArrayList有很多接口,一定是拓展了很多東西的,如果List接口沒有包含的方法,使用List接收的參數不能調用其他方法,但是使用ArrayList接收的話,所有ArrayList實現的方法都是可以調用的,只要是公開訪問即可.
ArrayList 遍歷方法
ArrayList 可以使用三方方式遍歷:for循環+下標、foreach、使用迭代器,還可以直接打印里面的內容。
int size = list.size();for (int i = 0; i < size; i++) {System.out.print(list.get(i) + " ");}System.out.println();
無論是Integer還是int接收元素,Java底層會幫我們自動拆箱的.
for (int x: list) {System.out.print(x + " ");}System.out.println();for (Integer y: list) {System.out.print(y + " ");}System.out.println();
ListIterator<Integer> it = list.listIterator(list.size());while (it.hasPrevious()) {System.out.print(it.previous()+" ");}System.out.println();
Java的ArrayList的父類是重寫過toString方法的.
System.out.println(list);
實踐
楊輝三角
這里要注意 List<List< Integer>> ,這個意思表示外面的list的元素是list,里面的list的元素是Integer,例如下圖所示:類似二維數組~
代碼:
class Solution {public List<List<Integer>> generate(int numRows) {List<List<Integer>> list = new ArrayList<>();List<Integer> list0 = new ArrayList<>();list0.add(1);list.add(list0);for(int i=1;i<numRows;i++) {List<Integer> list1 = new ArrayList<>();for(int j=0;j<=i;j++) {if(j==0 || j==i) {list1.add(1);} else {list1.add(list.get(i-1).get(j-1) + list.get(i-1).get(j));}}list.add(list1);}return list;}
}
洗牌功能實現
public class Card {private int number;private String cardColor;public Card(int number, String cardColor) {this.number = number;this.cardColor = cardColor;}@Overridepublic String toString() {return "Card{" +cardColor + '\'' +number +'}';}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Random;public class PlayCard {private static final String[] cardColor = {"?","?","?","?"};//購買52張牌public List<Card> buyCards() {List<Card> list = new ArrayList<>();for (int i = 1; i <= 13; i++) {for (int j = 0; j < 4; j++) {Card card = new Card(i,cardColor[j]);list.add(card);}}return list;}//洗牌public void shuffle(List<Card> list) {Random random = new Random();int size = list.size();for (int i = 0; i < size; i++) {int index = random.nextInt(size);//生成 0 ~ i-1 的隨機數Card card = list.get(index);list.set(index,list.get(i));list.set(i,card);}}//發牌public List<List<Card>> deal(List<Card> cards) {List<List<Card>> list = new ArrayList<>();//創建三個人List<Card> list0 = new ArrayList<>();List<Card> list1 = new ArrayList<>();List<Card> list2 = new ArrayList<>();list.add(list0);list.add(list1);list.add(list2);int size = cards.size();int size2 = list.size();int count = 0;boolean flag = true;while(flag) {for (int j = 0; j < size2; j++) {list.get(j).add(cards.remove(0));count++;if(count == size){flag = false;break;}}}return list;}
}
這里要注意隨機數的建立,首先先實例化Ramdom對象,然后使用nextInt方法,nextInt(int bound),生成的隨機數范圍是0~bound-1.