Java & Groovy & Scala & Kotlin - 13.數組與集合

Overview

本節主要介紹幾種語言中的數組和集合的對應用法。

數組在程序中一般用于表示一段連續的空間。通常來說數組的大小是預先指定的,數組的元素類型也是統一的,所以訪問數組時可以通過偏移量快速訪問任意一個元素。

集合類似數組,很多時候集合也是通過數組實現的,但是集合的長度是可變的,存儲的數據類型也可以不一樣(盡管一般都是存儲同類型的數據)。集合通常有兩種:List 和 Set,前者有序且元素可以重復,后者無序但元素不能重復。

Java 篇

數組

創建數組

定義一個數組的基本語法如下

類型[] 變量名 = new 類型[長度];

也可以在定義的同時指定包含的元素

類型[] 變量名 = {元素..};

例:

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

基本用法

修改數組

由于數組的長度是固定的,所以無法增加元素也無法刪除元素,只能對某一位置的元素進行修改

arr[0] = 10;

讀取某一位置的元素

System.out.println(arr[0]);

獲得數組長度

System.out.println(arr.length);

遍歷數組

for (int a : arr2) {
    System.out.println("Traverse " + a);
}

打印數組

Java 中默認的 toString() 只會打印出數組的地址,要想打印數組的內容需要使用工具類 Arrays 或者將其轉換為 List。

例:

System.out.println(Arrays.toString(arr2));

集合

List

List 表示列表,是集合中最常用的類型,Java 中的 List 默認是可變的,其本身是一個接口,最常用的是其子類 ArrayListLinkedList,前者用于隨機訪問,后者用于實現隊列。

創建 List
List<String> list = new ArrayList<String>();

默認創建的列表是空列表,里面沒有任何元素。這種兩邊都要寫明列表中元素類型的語法一直以來都被認為是非常丑陋的,所以 Java 7 以后提供了被稱作 Diamond 的語法,可以省略等式右邊的類型聲明。

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

事實上即使這樣比起 Guava 提供的用法 List<String> list = Lists.newArrayList(); 還是不太好看

需要注意的是在列表的工具類 Collections 中也有一個可以提供空列表的方法,但那個返回的是繼承自 AbstractListEmptyList 的實例,該實例不支持任何操作,與 new 出來的空列表并不一樣。

List emptyList = Collections.emptyList();
//  試圖調用以下方法會報 UnsupportedOperationException
//        emptyList.add(1);
修改 List

添加元素

list.add("Groovy");

也可以使用 Collections 的工具方法一次性添加多個元素

Collections.addAll(list, "Groovy", "Java", "Scala");

修改元素

list.set(0, "Ruby");

這里需要注意的是修改時的第一個參數為索引,索引不能超過該列表的長度,否則會報 IndexOutOfBoundsException。

刪除元素

//  刪除指定位置
list.remove(1);

//  刪除指定元素
list.remove("Ruby");
訪問元素
list.get(1);

索引只能是大于等于小于列表長度的整數,否則會報ArrayIndexOutOfBoundsException。

通過已有元素創建 List

List 有兩種方式可以在創建時就指定其存儲的內容

第一種是通過工具類 Arrays

List<String> list = Arrays.asList("Groovy", "Java", "Scala");

這種方式創建的其實是一個定長的列表,不支持任何會影響其長度的方法,比如說添加或者刪除項目均為報 UnsupportedOperationException。

第二種是通過初始化塊,這種方式創建的是普通的列表,但是不支持 Diamond 語法。

List<String> list = new ArrayList<String>() {{
            add("Groovy");
            add("Java");
            add("Scala");
        }};

不可變 List

在將列表作為返回值返回時,通常我們不希望列表內容被隨意修改,這時可以返回一個不可變列表來禁止任何修改行為。

List<String> immuatbleList = Collections.unmodifiableList(list);

以上方法實際是返回了一個 UnmodifiableCollection 的實例,該實例使用委托方式調用傳入的 list 對象,一旦發現執行了任何修改操作就立即拋出異常。

其它操作

獲得 List 的長度

System.out.println(list.size());

列表使用 size() ,數組使用 length,字符串使用 length(),這種設計個人認為是 Java API 中設計的很失敗的一個地方,但居然還有一些公司會拿這個作為面試的考題。

遍歷 List

for (String lang : list) {
    System.out.println("Traverse " + lang);
}

Set

Set 最常用的是其子類 HashSet,本質上其實是一個 key-value 都一致的 HashMap。由于用法類似,就不詳細舉例了。

Range

Java 不支持 Range

Groovy 篇

數組

創建數組

Groovy 定義數組類似 Java,但是指定具體元素時使用符號 []而不是 Java 的 {}

//  基本語法
def arr = new int[3]

//  定義時指定內容
def arr2 = [1, 2, 3] as int[]

注意第二個例子后面的 as int[],因為在 Groovy 中 [] 這種語法默認產生的其實時 List 對象,所以需要通過 as 語法將其轉換為數組對象。

基本用法

修改元素

arr[0] = 10

讀取元素

println(arr[0])

獲得數組長度

println(arr.length)

遍歷數組

for (int a : arr2) {
    println("Traverse " + a)
}

集合

List

Groovy 中的 List 與 Java 中的完全一致。

創建 List

空列表

def emptyList = []

指定元素的列表

def list = ["Groovy", "Java", "Scala"]

以上方式默認創建的是 ArrayList

修改 List

添加元素

list.add("Rust")

除了以上傳統方式,Groovy 還支持使用符號 << 來追加元素

list << "Kotlin"

修改元素

list[10] = "Ruby"
list.putAt(9, "Python")

與 Java 不同的是,Groovy 中如果指定的索引超過了列表的長度,列表會被自動擴容,所以以上做法在 Java 中是非法的,但是在 Groovy 中是允許的。

刪除元素

與 Java 完全相同

//  刪除指定位置
list.remove(1)

//  刪除自定元素
list.remove("Ruby")
訪問元素
list.get(2)
list[2]

以上兩種方式都可以,其中第二種可以讓數組和列表的訪問擁有統一的方式

同時 Groovy 還支持使用負數從后往前建立索引,或者使用 Range 獲得一個子列表

list[-1]  
list[-1..-3] 
通過已有元素創建 List

Groovy 可以使用 +- 從現有列表的元素創建新列表,這些方式不會修改原有列表,需要特別注意

def list = ["Groovy", "Java", "Scala", "Kotlin"]
def newList = list - ["Ruby", "Rust", "Kotlin"] + "Swift"

以上操作返回一個包含 Groovy, Java, Scala, Swift 的新列表。

不可變 List

使用方法同 Java 完全一致

展開操作符

Groovy 中可以使用展開操作符 *. 來操作列表中的每一個元素,實際上就相當于函數式編程中的 map 算子

def numbers = [1, 2, 3, 4, 3, 4]
def numbers2 = numbers*.plus(10)
println(numbers)            //[1, 2, 3, 4, 3, 4]
println(numbers2)           //[11, 12, 13, 14, 13, 14]

其它操作

獲得 List 長度

println(list.size())

遍歷 List

for (lang in list) {
    println("Traverse " + lang)
}

Set

同 Java 一致

Range

Range 表示一個連續范圍內的值。Range (范圍) 看起來有些像數組,但是數組只是存儲空間連續,值不需要連續。簡單來說數組可以是 1,10,2,3,399 ,而 Range 則必須是 1,2,3,41,3,5,7 這樣的數列。

定義一個 Range

def rng1 = 1..3
println(rng1)   //  [1, 2, 3]

Range 不僅可以用在數字上,也可以用在字符上

def rng4 = 'a'..'c'
println(rng4)   //  [a, b, c]

左閉右開

def rng2 = 1..<3
println(rng2)   //  [1, 2]

指定步長

def rng3 = (1..5).step(2)
println(rng3)   //  [1, 3, 5]

Scala 篇

數組

數組在 Scala 中可以分為定長數組 Array 和 變長數組 ArrayBuffer

Array

Array 在 Scala 中屬于傳統的定長數組。Scala 定義數組的方式類似 Java 中的列表,但是指定具體元素時即不是 Groovy 的 [] 也不是 Java 的{},而是 ()

//  基本語法
val arr = new Array[Int](3)

//  定義時指定內容
val arr2 = Array(1, 2, 3)

修改元素

arr(0) = 10

讀取元素

println(arr(0))

獲得數組長度

println(arr2.length)

遍歷數組

for (a <- arr2) {
  println(s"Traverse $a")
}

打印數組

默認 toString() 只會打印出數組的地址,要想打印數組的內容需要使用 mkString()

println(arr2.mkString(","))

ArrayBuffer

ArrayBuffer 在 Scala 中屬于變長數組,相比較數組而言其最大的缺點就是刪除元素時的效率較低,使用時相當于 Java 的 ArrayList。

定義 ArrayBuffer

val abuffer = ArrayBuffer[Int]()

修改元素

//  添加單個或多個元素
abuffer += 10

//  添加單個或多個 Array 或 ArrayBuffer
abuffer ++= arr2

//  刪除單個或多個元素
abuffer -= 3

讀取元素

println(abuffer(0))

Array 和 ArrayBuffer 的轉換

Array -> ArrayBuffer

val buffer = arr.toBuffer

ArrayBuffer -> Array

val arr4 = abuffer.toArray

集合

列表

Scala 中列表在語言層面就分為不可變列表和可變列表。不可變列表無法改變列表的內容,是 Scala 默認的列表類型。

不可變 List

定義一個不可變列表

空列表

val empty = List()
println(empty == Nil)   //  true

Scala 中空列表等于 Nil。且由于列表不可變,所以該空列表也無法進行任何寫操作

創建時指定元素

val list = List("Groovy", "Java", "Scala")

List 構造器

Scala 的列表結構與其它語言都不一樣。它分為頭部和尾部,頭部就是列表的第一個元素,尾部也是一個列表,它包含列表的其余部分。

val list = List("Groovy", "Java", "Scala")
println(list.head) // Groovy
println(list.tail) // List(Java, Scala)
println(list.tail.head) //  Java

由于是不可變列表,所以即無法添加修改元素,也無法刪除元素,但是可以通過符號 :: 結合當前列表返回新列表。

:: 是中綴操作符,左邊的操作數為組成新列表的元素,右操作數為當前列表,該操作符具有右結合性,即 A :: B :: C 會被翻譯成 A :: (B :: C)

val list = List("Groovy", "Java", "Scala")
val newList = "Ruby" :: list
//  val newList2 = list :: "Ruby"

以上第一個操作會返回包含 Ruby, Groovy, Java, Scala 的新列表。第二個操作則是非法的,因為右操作數 Ruby 不是列表。

掌握了右結合性的特性,我們就可以寫出如下代碼

val days = "Sunday" :: "Monday" :: "Tuesday" :: "Wednesday" :: "Thursday" :: "Friday" :: "Saturday" :: Nil

也可以通過操作符 ::: 結合兩個列表的內容產生新列表

val all = list ::: days
訪問元素
println(list(2)) // Scala

可變 List

可變 List 位于 scala.collection.mutable 包下,本質上是 LinkedList。

創建一個可變 List

var mutableList = new mutable.MutableList[Int]

可變 List 可以使用符號 += 添加新元素

//  添加單個元素
mutableList += 1

//  添加多個元素
mutableList +=(2, 3, 5)

訪問時與不可變 List 相同

println(mutableList(1))

盡管稱作可變 List,但是其并不支持直接刪除任意元素

ListBuffer

ListBuffer 是另一種可變 List,其本質實際是由 Nil:: 結合不可變 List 來實現的。

創建一個 ListBuffer

var listBuffer = new ListBuffer[Int]

可以使用符號 += 添加新元素或者符號 -= 刪除元素

//  添加單個元素
listBuffer += 1

//  添加多個元素
listBuffer +=(2, 3, 5)

//  刪除元素
listBuffer -= 2

訪問時與不可變 List 相同

println(listBuffer(1))
List 和 ListBuffer 轉換

List -> ListBuffer

list.toBuffer

ListBuffer -> List

listBuffer.toList

其它操作

獲得 List 長度

println(list.length)

注意,Scala 中列表和數組都統一使用了 length 獲取長度,解決了 Java 中那個糟糕的設計。

遍歷 List

for (lang <- list) {
  println(s"Traverse $lang")
}

Set

Set 使用方式類似 List,這里就不細說了。

Range

定義一個 Range

val rng1 = 1 to 3
println(rng1) //  Range(1, 2, 3)

Range 不僅可以用在數字上,也可以用在字符上

val rng4 = 'a' to 'c'
println(rng4) //  NumericRange(a, b, c)

左閉右開

val rng2 = 1 until 3
println(rng2) //  Range(1, 2)

指定步長

val rng3 = 1 to 5 by 2
println(rng3) //  Range(1, 3, 5)

除了以上方法,還可以直接通過構造器建立對象,需要注意的是通過構造器建立的范圍是左閉右開的

val rng5 = Range(1, 3)
println(rng5) //  Range(1, 2)

val rng6 = Range(1, 5, 2)
println(rng6) //  Range(1, 3)

Kotlin 篇

數組

創建數組

//  基本語法
val arr = arrayOfNulls<Int>(3)

//  定義時指定內容
val arr2 = arrayOf(1, 2, 3)

基本用法

修改元素

arr[0] = 10

讀取元素

println(arr[0])

獲得數組長度

println(arr.size

遍歷數組

for (a in arr2) {
    println("Traverse $a")
}

集合

列表

Kotlin 同 Scala 一樣 列表在語言層面就分為不可變列表和可變列表。不可變列表無法改變列表的內容,是 Kotlin 默認的列表類型。

不可變列表

定義一個不可變列表

空列表

val empty = emptyList<Int>()

Kotlin 中由于列表不可變,所以該空列表也無法進行任何寫操作

指定元素的列表

val list = listOf("Groovy", "Java", "Scala")
訪問元素
println(list(2)) // Scala

可變 List

老版本的 Kotlin 中可變 List 就是 LinkedList,但是 1.0 版本變成了 ArrayList,api 也跟著改變了。

創建一個可變 List

var mutableList = mutableListOf<String>()

可變 List 可以使用方法 add() 添加新元素,使用方法 remove() 刪除元素

mList.add("Ruby")

mList.remove("Java")

訪問時與不可變 List 相同

println(mutableList(1))

其它操作

獲得列表長度

println(list.size)

注意,Kotlin 和 Scala 一樣也統一了用法,但是 Kotlin 使用的是 size()

遍歷 List

for (lang in list) {
    println("Traverse $lang")
}

Set

Set 使用方式類似 List,這里就不細說了。

Range

定義一個 Range

val rng1 = 1..3
println(rng1)   //  1..3

Range 不僅可以用在數字上,也可以用在字符上

val rng4 = 'a'..'c'
println(rng4)   //  a..c

指定步長

val rng3 = (1..5).step(2)
println(rng3)   //  1..5 step 2

Summary

  • 除了 Java,其它語言都支持 Range 類型
  • Scala 與 Kotlin 中默認列表都是不可變形式
  • Scala 有可變數組和不可變數組兩種數組,其中可變數組是通過 ArrayList 實現的
  • Scala 有 Mutable List, Immutable List 和 ListBuffer 三種列表,其中 Mutable List 是通過 LinkedList 實現的, ListBuffer 是通過 Immutable List 和 Nil 實現的
  • Java 和 Groovy 訪問數組長度和列表長度分為為 lengthsize(),Scala 訪問數組和列表長度都使用 length,Kotlin 則都使用 size() ╮(╯_╰)╭

文章源碼見 https://github.com/SidneyXu/JGSK 倉庫的 _13_collection 小節

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Scala的集合類可以從三個維度進行切分: 可變與不可變集合(Immutable and mutable coll...
    時待吾閱讀 5,848評論 0 4
  • 前言 人生苦多,快來 Kotlin ,快速學習Kotlin! 什么是Kotlin? Kotlin 是種靜態類型編程...
    任半生囂狂閱讀 26,252評論 9 118
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 這篇講義只講scala的簡單使用,目的是使各位新來的同事能夠首先看懂程序,因為 scala 有的語法對于之前使用習...
    MrRobot閱讀 2,930評論 0 10
  • 春晚的經典歌曲《時間都去哪兒》令人印象深刻。你有關注過自己時間都花銷在哪里了嗎?你知道自己的時間黑洞都在哪里嗎?最...
    加一的修煉場閱讀 1,069評論 5 4