我们假设我们列出了一些项目清单,比如说Strings。
list 1: "a", "b", "c"
list 2: "d", "e", "f"
list 3: "1", "2", "3"
results: (a, d, 1), (a, d, 2), ... (c, f, 3)
(真正的用例与字符串无关,这只是一个模拟)
我写了一个递归方法来做到这一点,但我对它不满意,因为它创建了许多被抛出的临时集(是的,我知道java中的对象创建很便宜,通常比malloc中的cpu指令更少) C(来源:Java Concurrency in Action,p241),eden GC很便宜,等等等等。幽默我:)。
void combine(List<List<String>> itemLists, List<Set<String>> combinations, Set<String> partial) {
if (itemLists == null || itemLists.isEmpty()) return;
List<String> items = itemLists.get(0);
for (String s : items) {
Set<String> tmpSet = new HashSet<>(partial);
tmpSet.add(s);
if (itemLists.size() == 0) //termination test
combinations.add(tmpSet);
else
combine(itemLists.subList(1, itemLists.size()), combinations, tmpSet);
}
}
那么,你会怎么做呢?
编辑:要明确,我不想创建排列。我想创建sizeof(列表列表)大的集合。
答案 0 :(得分:3)
您正在寻找的是“笛卡儿产品”。
如果您使用“集”而不是“列表”,则可以使用Sets.cartesianProduct
。在迭代生成的列表时仍然会分配一些垃圾......但不会像其他方法那样多。
(请注意,作为一种常见的库方法,它经过了非常详尽的测试,因此您可以比使用SO中的几十行代码更有信心。)
仅供参考Lists.cartesianProduct
也有request,但我认为没有人在研究它。
答案 1 :(得分:1)
您需要一个包含所有可能集合的列表,其中只包含每个提供列表中的一个值,假设列表数量是可变的,并且这些列表的大小也是可变的。正确的吗?
那样的东西呢?
static List<Set<String>> combine(List<List<String>> itemLists)
{
// Calculate how many combinations we'll need to build
int remainingCombinations = itemLists.get(0).size();
for(int i=1; i<itemLists.size(); i++)
{
remainingCombinations *= itemLists.get(i).size();
}
List<Set<String>> allSets = new ArrayList<Set<String>>();
// Generate this combination
for (;remainingCombinations > 0; remainingCombinations --)
{
Set<String> currentSet = new HashSet<String>();
int positionInRow = remainingCombinations;
// Pick the required element from each list, and add it to the set.
for(int i=0; i<itemLists.size(); i++)
{
int sizeOfRow = itemLists.get(i).size();
currentSet.add(itemLists.get(i).get(positionInRow % sizeOfRow));
positionInRow /= sizeOfRow;
}
allSets.add(currentSet);
}
return allSets;
}
答案 2 :(得分:1)
这样效率更高:以与计算工作相同的方式处理它(每个“位置”是您的一个列表,每个“数字”可以进入该位置是列表的一个元素):
List<Set<String>> combine( List<List<String>> input ){
final int n = input.size();
int[] index = new int[n];
List<Set<Sting>> result = new List<>();
int position = 0;
while( position < n ){ // "overflow" check
// Add set to result.
Set<String> set = new HashSet<>();
for( int i=0; i<n; i++ )
set.add( input.get(i).get( index[i] ) );
result.add( set );
// Now the "hard" part: increment the index array
position = 0;
while( position < n ){
if( index[ position ] < input.get( position ).size() ){
index[position]++;
break;
}
else // carry
index[ position++ ] = 0;
}
}
return result;
}
(未经测试,可能有一些错误,但主要的想法是那里)。 通常,递归比迭代慢。