Java刷題時常用的標準庫資料結構和相應操作

CodeReaper發表於2022-03-03

一、線性表(廣義的陣列)

在演算法題中,我們一般使用到的線性表一般有兩種,且它們的優缺點如下:

  • 陣列
    • 優點:可以使用[]運算子進行隨機讀寫
    • 缺點:陣列大小固定,不能動態新增資料
  • List物件
    • 優點:可以動態新增資料
    • 缺點:讀寫資料需要使用get(int index)set(int index, Object object),和陣列相比比較麻煩

1. 陣列

這裡陣列的主要用法和c++比較類似,這裡主要寫一下一些特殊的操作以及Arrays工具類提供的一些方法。

一維陣列的定義和初始化

① 直接指定固定大小:

int[] arr = new int[n];

則開闢的空間會填充上預設值:

  • 數值型別填充0
  • boolean型別填充false
  • 物件型別填充null

② 定義時進行初始化

int[] arr = new int[]{1, 2, 3, 4, 5};

二維陣列的定義和初始化

① 和一維陣列一樣直接給定兩個維度的大小(行數和列數):

int[][] matrix = new int[m][n];

② 和c++類似,二維陣列也可以像c++中的type** matrix一樣,先給第一個維度分配空間,然後再為第二個維度分配不同的空間,例如下面的程式碼分配下三角矩陣:

int n = 5;
int[][] matrix = new int[n][];
for (int i = 0; i < n; i++) {
    matrix[i] = new int[i + 1];
}

列印展示:

image-20220303213535341

Arrays工具類的一些常用方法

在Java中原生陣列實際上並不是完全的物件導向的,對於List物件希望進行某項操作只需要使用.+方法即可,但陣列型別本身卻沒有帶有這些操作,因而Arrays工具類填補了這部分的空白。

Arrays.fill()

Arrays.fill有兩個常見的使用:

  • Arrays.fill(int[] array, int value)
  • Arrays.fill(int[] array, int start, int end, int val)

這個函式是用於填充陣列的,第一個引數是陣列,第二個引數是填充的值,而第二種用法規定了填充的起止下標:[start, end)

Arrays.sort()

排序函式,一般也有兩種引數填充方法:

  • Arrays.sort(int[] array)
  • Arrays.sort(Object[] array, Comparator c) tips 只有物件陣列才能使用這種方式

第一種方式是按照預設的方式進行排序,數字型別按從小到大排,字串型別按字典序排,而第二種方式中填寫的第二個引數是用於改變預設的排序規則的,例如這裡我希望從大到小排序():

Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
Arrays.sort(arr, new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
});
// 或者使用下面的更加簡潔的lambda表示式
Arrays.sort(arr, (num1, num2) -> num2 - num1);

由於新增Comparator物件的這種方式第一個引數只能是物件陣列,因此我這裡不再使用int[]而是改為了使用Integer[]

這裡就會出現一個這樣的需求:如果我原來的型別是int[],那麼如何轉換為Integer[]呢?這裡我們可以使用下面的程式碼進行轉化(其他的例如boolean到Boolean也可以按照如下方式轉化):

int[] arrOrigin = new int[]{1, 2, 3, 4, 5};
Integer[] arr = (Integer[]) Arrays.stream(arrOrigin).boxed().toArray();

即將原陣列轉為stream物件後呼叫boxed方法得到Stream<Integer>,最後再呼叫Stream類中的toArray()成員方法即可從流重新轉為陣列。而Integer[]想要轉為int[]則需要呼叫Stream類的mapToInt(Integer::intValue).toArray()

參考資料:https://codingdict.com/questions/3373

Arrays.toString(int[] array)

這個方法可以得到陣列完整的內容,而如果直接使用arr.toString()只會得到物件的地址等無用資訊。

Arrays.asList(int[] array)

將陣列轉為List物件:

Integer[] arr = new Integer[]{1, 2, 3, 4, 5};
List<Integer> list = Arrays.asList(arr);

此外這個函式還可以分散填寫各個List物件初始元素的值:

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

⑤ 反轉陣列

這裡可以使用工具類反轉的陣列也僅限是物件陣列非物件陣列可能只能手寫反轉演算法了),參考程式碼如下:

String[] strArr = new String[]{"e", "d", "c", "b", "a"};
Collections.reverse(Arrays.asList(strArr));

輸出結果:

[a, b, c, d, e]

另外還有一些例如Arrays.copyOf等方法不太常用,這裡不再詳細介紹。

2. List介面容器

物件的構建

實現類一般使用ArrayList(此外還有LinkedList,但不常用),構造物件方式如下:

List<Integer> list = new ArrayList<>();

讀寫和插入刪除資料

讀:E get(int index)

寫:E set(int index, E element)

插入:boolean add(E e)void add(int index, E element)

刪除:boolean remove(Object o)E remove(int index)

排序

使用List的介面方法sort(Comparator c)(數字從大到小排):

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
list.sort((num1, num2) -> num2 - num1);

輸出:

[5, 4, 3, 2, 1]

或者也可以使用Collections.sort(List l, Comparator c),可以達到相同的效果。

反轉陣列

// Collections工具類靜態方法:Collections.reverse(List list)
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Collections.reverse(list);

List轉為陣列

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
Integer[] arr = (Integer[]) list.toArray();

二、字串

以下api如果沒有標註則預設為String類成員方法。

操作 api 說明
獲取長度 int length()
獲取下標對應字元 char charAt(int index)
轉換為字元陣列 char[] toCharArray()
取子串 String substring(int beginIdx, int endIdx) 其中endIdx是可選的
轉為int等數字型別 靜態方法 Integer.parseInt(String str) 這是int型別的,其他型別以此類推
數值型別轉為字串 靜態方法 String.valueOf(T val) 這裡的T可以是int、double等型別
按分隔符切分字串 String[] split(String regex) 填入的是正規表示式
反轉字串 StringBuilder成員方法 string reverse() 需要藉助StringBuilder

這些容器類基本都會包含有獲取元素數量(size())和判斷是否為空(empty()isEmpty())等相同的方法,因此後面的api表格只列出該類特有的操作。

三、Map和Set

1. Map

Map這裡一般使用的實現為HashMap,少數需要按照鍵進行排序時使用到TreeMap,構造物件如下:

Map<String, String> map = new HashMap<>();

常用操作和api:

操作 api
V get(Object key)
V put(K key, V value)
是否包含key boolean containsKey(Object key)
是否包含value boolean containsValue(Object value)

遍歷Map:

// 1. 使用forEach + lambda表示式(推薦)
map.forEach((key, value)-> {
	...
});
// 2. 使用for結合keySet()
for (String key : map.keySet()) {
    String value = map.get(key);
    ...
}

2. Set

和Map類似,這裡Set的實現一般也選擇HashSet,少數需要按照鍵進行排序時使用到TreeSet,構造物件如下:

Set<String> set = new HashSet<>();

常用操作和api:

操作 api
插入元素 boolean add(E e)
刪除元素 boolean remove(Object o)
是否包含元素 boolean contains(Object o)

同理,set也可以使用forEach+lambda以及增強for兩種寫法遍歷元素。

四、棧Stack和佇列Queue

1. 棧Stack

構造物件:

Stack<String> stack = new Stack<>();

常用操作和api:

操作 api
壓棧 E push(E item)
彈棧 E pop()
檢視棧頂元素 E peek()

2. 佇列Queue

Queue是一個介面,一般實現類取ArrayDeque。構造物件程式碼如下:

Queue<String> queue = new ArrayDeque<>();

常用操作和api:

操作 api
入隊 boolean offer(E e)
出隊 E poll()
檢視隊首 E peek()

五、優先佇列

構造物件:

PriorityQueue<Integer> heap = new PriorityQueue<>();

使用無引數建構函式時得到的是小頂堆,如果我們希望得到大頂堆則需要填入一個Comparator引數,如下為構造int型別大頂堆的方式:

PriorityQueue<Integer> heap = new PriorityQueue<>((o1, o2) -> o2 - o1);

其中填入的lambda表示式為新建Comparator匿名內部類的語法糖。

常用操作和api:

操作 api
入隊 boolean offer(E e)
出隊 boolean poll(E e)
檢視隊首(堆頂) E peek()

相關文章