如何使用Arrow Optics增删不可变列表中的元素?
针对不可变数据结构中列表的简洁修改方案
1. 借助kotlinx.collections.immutable库(推荐)
这个库专门为Kotlin提供了不可变集合的原生修改方法,完全替代手动拼接子列表的冗余操作:
- 先将你的列表替换为库中的
ImmutableList(可通过list.toImmutableList()转换) - 直接调用
add(index, element)或removeAt(index)方法,这些方法会返回新的不可变列表实例,完全符合不可变数据的设计原则 - 结合数据类的
copy方法更新整个结构,示例代码:import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toImmutableList data class Bar(val list: ImmutableList<String>) data class Foo(val bar: Bar) val foo: Foo = Foo(Bar(listOf("a", "b", "c").toImmutableList())) val i = 1 val newElement = "x" // 简洁修改列表并更新整个结构 val newFoo = foo.copy( bar = foo.bar.copy( list = foo.bar.list.add(i, newElement) ) )
2. 自定义扩展函数封装拼接逻辑
如果不想引入第三方库,可给标准List写通用扩展函数,把重复的拼接逻辑封装起来:
fun <T> List<T>.addAt(index: Int, element: T): List<T> { require(index in 0..size) { "Index $index out of bounds for list of size $size" } return subList(0, index) + element + subList(index, size) } fun <T> List<T>.removeAt(index: Int): List<T> { require(index in 0..<size) { "Index $index out of bounds for list of size $size" } return subList(0, index) + subList(index + 1, size) }
之后修改列表时直接调用扩展函数,代码可读性大幅提升:
val newFoo = foo.copy( bar = foo.bar.copy( list = foo.bar.list.addAt(i, newElement) ) )
3. 结合透镜(Lens)实现深层修改
如果你的项目已经在使用透镜(比如自定义实现或Arrow等库),可以扩展透镜支持列表的批量修改:
自定义透镜示例
// 基础透镜定义 data class Lens<A, B>( val get: (A) -> B, val set: (A, B) -> A ) // 给列表类型的透镜扩展添加元素的方法 fun <A, B> Lens<A, List<B>>.addAt(index: Int, element: B): (A) -> A { return { target -> val currentList = get(target) set(target, currentList.addAt(index, element)) } } // 定义指向foo.bar.list的透镜 val fooBarListLens = Lens( get = { foo: Foo -> foo.bar.list }, set = { foo, newList -> foo.copy(bar = foo.bar.copy(list = newList)) } ) // 使用透镜修改 val newFoo = fooBarListLens.addAt(i, newElement)(foo)
4. 利用Traversal/Index DSL(Arrow库)
Arrow库的Traversal和Index类型可以直接针对集合的索引位置进行操作,无需手动处理整个列表:
import arrow.optics.Lens import arrow.optics.modify // 定义深层透镜 val fooBar: Lens<Foo, Bar> = Lens( get = { it.bar }, set = { foo, bar -> foo.copy(bar = bar) } ) val barList: Lens<Bar, List<String>> = Lens( get = { it.list }, set = { bar, list -> bar.copy(list = list) } ) val fooBarList = fooBar compose barList // 修改指定位置的列表(结合自定义扩展或ImmutableList) val newFoo = fooBarList.modify(foo) { it.addAt(i, newElement) }
内容的提问来源于stack exchange,提问作者Todd O'Bryan
相关产品推荐
相关产品推荐

