生成无重复5行数字组合遇内存错误,求优化方案
解决5行无重复数字组合生成的内存溢出问题
问题背景
需要生成0-100范围内无重复数字的5行组合(例如1,2,3,4,5、1,2,3,4,6这类所有可能的组合)。当仅生成3行组合时功能正常,但添加第4、5行后,因组合总量达到约1200万条,出现内存错误。核心问题是一次性加载所有组合导致内存耗尽,而非代码逻辑错误,需要通过分批生成/懒加载的方式优化。
原代码问题分析
原代码存在两个关键问题:
- 内存占用过高:通过嵌套
flatMap+map直接生成所有组合并转换为List,1200万条字符串会一次性存入内存,远超移动端应用的内存上限。 - 代码冗余:手动定义
rowOne到rowFive的字符串列表,重复且易出错;同时未做重复数字过滤,可能生成包含重复数字的组合(例如2,2,3,4,5这类不符合需求的结果)。
原代码如下:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val startButton = findViewById<Button>(R.id.startButton) val possibilities = findViewById<TextView>(R.id.possibilities) val rowOne = listOf("1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70") val rowTwo = listOf("2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70") val rowThree = listOf("3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70") val rowFour = listOf("4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70") val rowFive = listOf("5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20","21","22","23","24","25","26","27","28","29","30","31","32","33","34","35","36","37","38","39","40","41","42","43","44","45","46","47","48","49","50","51","52","53","54","55","56","57","58","59","60","61","62","63","64","65","66","67","68","69","70") val combinations = rowOne.flatMap { rowOne -> rowTwo.flatMap { rowTwo -> rowThree.flatMap { rowThree -> rowFour.flatMap { rowFour -> rowFive.map { rowFive -> "$rowFour $rowFive" } }.map { resultThree -> "$resultThree $rowThree" } }.map { resultTwo -> "$resultTwo $rowTwo" } }.map { resultOne -> "$resultOne $rowOne" } }.toList() startButton.setOnClickListener { possibilities.text = ("${(combinations).size} Possibilities")} } }
优化方案
1. 用Sequence实现懒加载
Kotlin的Sequence是懒加载的集合,不会一次性计算所有元素,而是按需生成,能大幅降低内存占用。将原代码中的List操作替换为Sequence,避免一次性加载所有组合。
2. 过滤重复数字
通过强制后续数字大于前一个数字的方式,确保组合内无重复数字(例如r2 > r1、r3 > r2等),同时简化行的生成逻辑。
3. 分批处理组合
如果需要对组合进行存储/展示,可按固定批次处理(例如每10000条为一批),处理完成后立即释放该批次内存,避免累积占用。
优化后的代码
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val startButton = findViewById<Button>(R.id.startButton) val possibilities = findViewById<TextView>(R.id.possibilities) // 生成数字范围的字符串列表,替代手动定义 val numRange = 1..70 val numStrings = numRange.map { it.toString() } // 用Sequence生成无重复的5行组合,懒加载 val combinationsSequence = numStrings.asSequence().flatMap { r1 -> // 过滤出比r1大的数字,避免重复 numStrings.filter { it > r1 }.asSequence().flatMap { r2 -> numStrings.filter { it > r2 }.asSequence().flatMap { r3 -> numStrings.filter { it > r3 }.asSequence().flatMap { r4 -> numStrings.filter { it > r4 }.asSequence().map { r5 -> "$r1 $r2 $r3 $r4 $r5" } } } } } startButton.setOnClickListener { // 计算总组合数,无需生成所有元素 val totalCount = combinationsSequence.count() possibilities.text = "$totalCount Possibilities" // 可选:分批处理组合(示例:每10000条处理一次) val batchSize = 10000 var currentBatch = mutableListOf<String>() combinationsSequence.forEachIndexed { index, combo -> currentBatch.add(combo) if ((index + 1) % batchSize == 0) { // 这里替换为你的业务逻辑,比如写入文件、展示等 processBatch(currentBatch) currentBatch.clear() } } // 处理剩余的不足一批的组合 if (currentBatch.isNotEmpty()) { processBatch(currentBatch) } } } // 自定义批次处理逻辑 private fun processBatch(batch: List<String>) { // 示例:打印批次大小,实际可替换为写入文件等操作 Log.d("CombinationBatch", "Processed ${batch.size} combinations") } }
关键优化点说明
- 懒加载:
asSequence()将集合转换为序列,所有flatMap、filter、map操作都不会立即执行,直到调用count()或forEach()时才按需生成元素。 - 内存控制:分批处理时,每批处理完成后立即清空列表,释放内存,避免1200万条数据同时占用内存。
- 代码简化:用
numRange生成数字字符串列表,替代手动重复定义,减少冗余代码。
内容的提问来源于stack exchange,提问作者Jeremy Kline
相关产品推荐
相关产品推荐

