วิธีการโคลนหรือคัดลอกรายการใน kotlin


111

จะคัดลอกรายการใน Kotlin ได้อย่างไร?

ฉันกำลังใช้

val selectedSeries = mutableListOf<String>()
selectedSeries.addAll(series)

มีวิธีที่ง่ายกว่านี้ไหม


1
ฉันคิดว่าวิธีแก้ปัญหาของคุณเป็นวิธีที่ง่ายที่สุดอยู่แล้วในกรณีที่คุณไม่ต้องการการโคลนนิ่งแบบลึก
Serdar Samancıoğlu

การคัดลอกรายการเพียงแค่คัดลอกการอ้างอิงไปยังรายการ ไอเท็มจะไม่โคลน อย่าลืมโคลนรายการในระหว่างการคัดลอกรายการหากคุณต้องการมีรายการโคลนลึก
CoolMind

คำตอบ:


156

ใช้งานได้ดี

val selectedSeries = series.toMutableList()

6
val selectedSeries = series.toList()ยังใช้งานได้เนื่องจากมีการเรียกtoMutableList()ใช้งาน
Flávio Faria

4
@ FlávioFariaเพิ่งทดสอบด้วย===และต้องบอกtoList()ว่าไม่ได้คัดลอกคอลเลกชัน แต่toMutableList()ทำ
Peppermint Paddy

3
@PeppermintPaddy มันไม่สำเนายกเว้นในกรณีของรายการที่ว่างเปล่า ถ้าแหล่งที่มาว่างเปล่าIterable.toList()จะส่งคืนemptyList()ซึ่งจะส่งคืนวัตถุเดียวกัน (ไม่เปลี่ยนรูป) เสมอ ดังนั้นหากคุณทดสอบด้วยemptyList()คุณจะได้วัตถุเดียวกันกลับมา
Laurence Gonsalves

4
นี่ไม่ใช่คำตอบที่ดีและไม่ใช่คำตอบที่ถูกต้องไม่มีการรับประกันว่าการใช้งานในอนาคตอาจเปลี่ยนแปลงได้เว้นแต่จะมีการบันทึกไว้เป็นพิเศษว่าการเรียกใช้เมธอดนี้จะส่งคืนสำเนาใหม่เสมอ
Bhargav

1
@BrunoJCM นั่นไม่ใช่อีกต่อไป สถานะเอกสารของ Kotlin ที่toMutableList()ส่งคืนรายการใหม่ "ส่งคืน MutableList ใหม่ที่เต็มไปด้วยองค์ประกอบทั้งหมดของคอลเล็กชันนี้"
Pär Nils Amsen

26

คุณสามารถใช้ได้

รายการ -> toList ()

อาร์เรย์ -> toArray ()

ArrayList -> toArray ()

MutableList -> toMutableList ()


ตัวอย่าง:

val array = arrayListOf("1", "2", "3", "4")

val arrayCopy = array.toArray() // copy array to other array

Log.i("---> array " ,  array?.count().toString())
Log.i("---> arrayCopy " ,  arrayCopy?.count().toString())

array.removeAt(0) // remove first item in array 

Log.i("---> array after remove" ,  array?.count().toString())
Log.i("---> arrayCopy after remove" ,  arrayCopy?.count().toString())

พิมพ์บันทึก:

array: 4
arrayCopy: 4
array after remove: 3
arrayCopy after remove: 4

16

ฉันสามารถหาทางเลือกได้สองทาง:

1. val selectedSeries = mutableListOf<String>().apply { addAll(series) }

2. val selectedSeries = mutableListOf(*series.toTypedArray())

อัปเดต: ด้วยเอ็นจิ้นการอนุมานประเภทใหม่ (เลือกใช้ใน Kotlin 1.3) เราสามารถละเว้นพารามิเตอร์ประเภททั่วไปในตัวอย่างที่ 1 และมีสิ่งนี้:

1. val selectedSeries = mutableListOf().apply { addAll(series) }

FYI วิธีเลือกใช้ Inference ใหม่kotlinc -Xnew-inference ./SourceCode.ktสำหรับบรรทัดคำสั่งหรือkotlin { experimental { newInference 'enable'}สำหรับ Gradle สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการอนุมานประเภทใหม่โปรดดูวิดีโอนี้: KotlinConf 2018 - การอนุมานประเภทใหม่และคุณลักษณะภาษาที่เกี่ยวข้องโดย Svetlana Isakovaโดยเฉพาะอย่างยิ่ง 'การอนุมานสำหรับผู้สร้าง' ที่ 30 '


ควรจะแบ่งออกเป็น 2 คำตอบเพราะฉันคิดว่าคำตอบแรกถูกต้อง แต่คำตอบหลังขาดความสวยงาม
Holger Brandl

@ จาค็อบวู: ฉันประหลาดใจที่เห็นว่าสัญลักษณ์ * ในโซลูชันที่สองไม่ก่อให้เกิดข้อผิดพลาด มันทำอะไร? ฉันทำการค้นหาด้วย "การคูณยูนารี" แต่ไม่พบอะไรเลย
Lensflare

1
@Lensflare * หมายถึงการทำลายอาร์เรย์ออกเป็นรายการแยกกันเช่น mutableListOf (* [1, 2, 3]) หมายถึง mutableListOf (1, 2, 3) มันเหมือนกับการดำเนินการที่ตรงกันข้ามกับ vararg
Jacob Wu

1
@Jacob Wu: ขอบคุณครับ จากคำตอบของคุณเราพบว่าตัวดำเนินการนี้เรียกว่า "ตัวดำเนินการกระจาย" ฉันเห็นว่ามันช่วยได้อย่างไรโดยรวมพารามิเตอร์บางตัวกับอาร์เรย์ลงในรายการ varargs แต่ตัวอย่างของคุณมีประโยชน์อะไร? เร็วขึ้นหรืออะไร? หรือเป็นกุญแจสำคัญในการตรวจสอบว่ามีการคัดลอกคอลเล็กชันหรือไม่
Lensflare

@Lensflare ฉันคิดว่าประโยชน์เป็นเพียงไวยากรณ์เท่านั้น - รหัสสั้นและไม่จำเป็นต้องมีประเภททั่วไปที่ชัดเจน (เช่นในตัวอย่างที่ 1 ของฉัน) เบื้องหลังฉันเชื่อว่าโค้ดถูกคอมไพล์ไปยังการทำงานของอาร์เรย์ดังนั้นประสิทธิภาพควรจะเหมือนกัน
Jacob Wu

14

หากรายการของคุณมีคลาสข้อมูล kotlinคุณสามารถทำได้

selectedSeries = ArrayList(series.map { it.copy() })

จะเป็นอย่างไรหากคุณต้องการคัดลอกเพียง 1 แอตทริบิวต์ของอาร์เรย์ไปยังรายการอาร์เรย์อื่น
Ahmad Shahwaiz

9

คุณสามารถใช้ส่วนขยายที่ให้มาIterable.toMutableList()ซึ่งจะให้รายชื่อใหม่แก่คุณ น่าเสียดายที่ลายเซ็นและเอกสารแนะนำมีขึ้นเพื่อให้แน่ใจว่า an Iterableเป็นList(เช่นเดียวกับtoStringและto<type>วิธีการอื่น ๆ อีกมากมาย) ไม่มีอะไรรับประกันคุณได้ว่าจะเป็นรายการใหม่ ตัวอย่างเช่นการเพิ่มบรรทัดต่อไปนี้ที่จุดเริ่มต้นของส่วนขยาย: if (this is List) return thisเป็นการปรับปรุงประสิทธิภาพที่ถูกต้อง (หากปรับปรุงประสิทธิภาพได้จริง)

นอกจากนี้เนื่องจากชื่อรหัสผลลัพธ์จึงไม่ชัดเจนนัก

ฉันต้องการเพิ่มส่วนขยายของตัวเองเพื่อให้แน่ใจในผลลัพธ์และสร้างโค้ดที่ชัดเจนมากขึ้น (เช่นเดียวกับที่เรามีสำหรับอาร์เรย์ ):

fun <T> List<T>.copyOf(): List<T> {
    val original = this
    return mutableListOf<T>().apply { addAll(original) }
}

fun <T> List<T>.mutableCopyOf(): MutableList<T> {
    val original = this
    return mutableListOf<T>().apply { addAll(original) }
}

ทราบว่าaddAllเป็นวิธีที่เร็วที่สุดที่จะคัดลอกเพราะใช้ภาษาในการดำเนินการSystem.arraycopyArrayList

นอกจากนี้โปรดระวังว่าสิ่งนี้จะทำให้คุณได้สำเนาตื้น ๆเท่านั้น


ฉันชอบวิธีนี้ ไม่ควรaddAll(this@copyOf)เพราะthisข้างในapplyจะอ้างถึงรายการว่างที่สร้างขึ้นใหม่? อย่างนั้นหรือmutableListOf<T>().also { it.addAll(this) }?
Franko Leon Tokalić

5

สำหรับสำเนาตื้นฉันขอแนะนำ

.map{it}

ซึ่งจะใช้ได้กับคอลเลกชันหลายประเภท


1
โปรดทราบว่ามันใช้ไม่ได้กับMaps มันรวบรวม แต่เนื่องจากitเป็น a Map.Entryและสำเนาตื้นคุณจึงมีรายการเดียวกัน
noamtm

1
@noam ™ใช่นั่นคือสิ่งที่ฉันหมายถึงกับสำเนาตื้น ๆ วิธีนี้จะไม่คัดลอกรายการ จะทำสำเนาคอลเล็กชันที่มีรายการเดียวกันเท่านั้น แผนที่ไม่มีอะไรพิเศษที่นี่
Lensflare

2
ประเด็นของฉันคือแม้ว่ามันจะดึงดูดให้ใช้บนแผนที่ด้วย แต่มันก็รวบรวมและดูเหมือนจะใช้งานได้ - มันไม่ได้ผลจริงๆ
noamtm

4

เช่นเดียวกับใน Java:

รายการ:

    val list = mutableListOf("a", "b", "c")
    val list2 = ArrayList(list)

แผนที่:

    val map = mutableMapOf("a" to 1, "b" to 2, "c" to 3)
    val map2 = HashMap(map)

สมมติว่าคุณกำหนดเป้าหมายเป็น JVM (หรือ Android) ฉันไม่แน่ใจว่ามันใช้ได้กับเป้าหมายอื่นเนื่องจากมันต้องอาศัยตัวสร้างสำเนาของ ArrayList และ HashMap


2

ผมจะใช้วิธีขยาย :toCollection()

val original = listOf("A", "B", "C")
val copy = original.toCollection(mutableListOf())

สิ่งนี้จะสร้างใหม่MutableListแล้วเพิ่มแต่ละองค์ประกอบของต้นฉบับในรายการที่สร้างขึ้นใหม่

MutableList<String>สรุปประเภทที่นี่จะเป็น หากคุณไม่ต้องการเปิดเผยความไม่แน่นอนของรายการใหม่นี้คุณสามารถประกาศประเภทอย่างชัดเจนว่าเป็นรายการที่ไม่เปลี่ยนรูปได้:

val copy: List<String> = original.toCollection(mutableListOf())


0

สำหรับรายการง่ายๆมีวิธีแก้ไขปัญหาที่ถูกต้องมากมายด้านบน

อย่างไรก็ตามเป็นเพียงรายการตื้น ๆ

ฟังก์ชั่นด้านล่างทำงานสำหรับการใด ๆ 2 ArrayListมิติ คือในทางปฏิบัติเทียบเท่ากับArrayList MutableListที่น่าสนใจคือมันใช้ไม่ได้เมื่อใช้MutableListประเภทโจ่งแจ้ง หากต้องการมิติข้อมูลเพิ่มเติมจำเป็นต้องสร้างฟังก์ชันเพิ่มเติม

fun <T>cloneMatrix(v:ArrayList<ArrayList<T>>):ArrayList<ArrayList<T>>{
  var MatrResult = ArrayList<ArrayList<T>>()
  for (i in v.indices) MatrResult.add(v[i].clone() as ArrayList<T>)
  return MatrResult
}

การสาธิตเมทริกซ์จำนวนเต็ม:

var mat = arrayListOf(arrayListOf<Int>(1,2),arrayListOf<Int>(3,12))
var mat2 = ArrayList<ArrayList<Int>>()
mat2 = cloneMatrix<Int>(mat)
mat2[1][1]=5
println(mat[1][1])

มันแสดงให้เห็น 12


-2

ลองใช้โค้ดด้านล่างเพื่อคัดลอกรายการในKotlin

arrayList2.addAll(arrayList1.filterNotNull())
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.