如何在 Java 中创建值组合?
Posted
技术标签:
【中文标题】如何在 Java 中创建值组合?【英文标题】:How to create combinations of values in Java? 【发布时间】:2016-06-27 22:25:09 【问题描述】:我有以下地图:Map<Integer,String[]> map = new HashMap<Integer,String[]>();
键是整数,值是数组(也可以用列表代替)。
现在,我想获取键中值的所有可能组合。例如,假设地图包含以下条目:
key 1: "test1", "***"
key 2: "test2", "wow"
key 3: "new"
组合包括
("test1","test2","new")
("test1","wow","new")
("***", "test2", "new")
("***", "wow", "new")
为此,我设想了一个方法 boolean hasNext()
,如果存在下一对,则返回 true,而第二个方法仅返回下一组值(如果有)。
如何做到这一点?该地图也可以被其他数据结构替换。
【问题讨论】:
这可以使用递归来实现,但是如何......这仍然是一个有待回答的问题...... Nahh :) 您可以轻松地做到这一点而无需递归。只需计算一个“变量”基数。 【参考方案1】:我将此作为一个挑战,看看新的 Java 8 API 是否有助于解决这类问题。所以这是我的解决方案:
public class CombinatorIterator implements Iterator<Collection<String>>
private final String[][] arrays;
private final int[] indices;
private final int total;
private int counter;
public CombinatorIterator(Collection<String[]> input)
arrays = input.toArray(new String[input.size()][]);
indices = new int[arrays.length];
total = Arrays.stream(arrays).mapToInt(arr -> arr.length)
.reduce((x, y) -> x * y).orElse(0);
counter = 0;
@Override
public boolean hasNext()
return counter < total;
@Override
public Collection<String> next()
List<String> nextValue = IntStream.range(0, arrays.length)
.mapToObj(i -> arrays[i][indices[i]]).collect(Collectors.toList());
//rolling carry over the indices
for (int j = 0;
j < arrays.length && ++indices[j] == arrays[j].length; j++)
indices[j] = 0;
counter++;
return nextValue;
请注意,我不使用地图作为输入,因为地图键实际上在这里没有任何作用。您可以使用map.values()
来传递迭代器的输入。使用以下测试代码:
List<String[]> input = Arrays.asList(
new String[] "such", "nice", "question",
new String[] "much", "iterator",
new String[] "very", "wow"
);
Iterator<Collection<String>> it = new CombinatorIterator(input);
it.forEachRemaining(System.out::println);
输出将是:
[such, much, very]
[nice, much, very]
[question, much, very]
[such, iterator, very]
[nice, iterator, very]
[question, iterator, very]
[such, much, wow]
[nice, much, wow]
[question, much, wow]
[such, iterator, wow]
[nice, iterator, wow]
[question, iterator, wow]
【讨论】:
【参考方案2】:该算法本质上与十进制数的增量算法(“x -> x+1”)几乎相同。
这里是迭代器类:
import java.util.Iterator;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.TreeSet;
public class CombinationsIterator implements Iterator<String[]>
// Immutable fields
private final int combinationLength;
private final String[][] values;
private final int[] maxIndexes;
// Mutable fields
private final int[] currentIndexes;
private boolean hasNext;
public CombinationsIterator(final Map<Integer,String[]> map)
combinationLength = map.size();
values = new String[combinationLength][];
maxIndexes = new int[combinationLength];
currentIndexes = new int[combinationLength];
if (combinationLength == 0)
hasNext = false;
return;
hasNext = true;
// Reorganize the map to array.
// Map is not actually needed and would unnecessarily complicate the algorithm.
int valuesIndex = 0;
for (final int key : new TreeSet<>(map.keySet()))
values[valuesIndex++] = map.get(key);
// Fill in the arrays of max indexes and current indexes.
for (int i = 0; i < combinationLength; ++i)
if (values[i].length == 0)
// Set hasNext to false if at least one of the value-arrays is empty.
// Stop the loop as the behavior of the iterator is already defined in this case:
// the iterator will just return no combinations.
hasNext = false;
return;
maxIndexes[i] = values[i].length - 1;
currentIndexes[i] = 0;
@Override
public boolean hasNext()
return hasNext;
@Override
public String[] next()
if (!hasNext)
throw new NoSuchElementException("No more combinations are available");
final String[] combination = getCombinationByCurrentIndexes();
nextIndexesCombination();
return combination;
private String[] getCombinationByCurrentIndexes()
final String[] combination = new String[combinationLength];
for (int i = 0; i < combinationLength; ++i)
combination[i] = values[i][currentIndexes[i]];
return combination;
private void nextIndexesCombination()
// A slightly modified "increment number by one" algorithm.
// This loop seems more natural, but it would return combinations in a different order than in your example:
// for (int i = 0; i < combinationLength; ++i)
// This loop returns combinations in the order which matches your example:
for (int i = combinationLength - 1; i >= 0; --i)
if (currentIndexes[i] < maxIndexes[i])
// Increment the current index
++currentIndexes[i];
return;
else
// Current index at max:
// reset it to zero and "carry" to the next index
currentIndexes[i] = 0;
// If we are here, then all current indexes are at max, and there are no more combinations
hasNext = false;
@Override
public void remove()
throw new UnsupportedOperationException("Remove operation is not supported");
这里是示例用法:
final Map<Integer,String[]> map = new HashMap<Integer,String[]>();
map.put(1, new String[]"test1", "***");
map.put(2, new String[]"test2", "wow");
map.put(3, new String[]"new");
final CombinationsIterator iterator = new CombinationsIterator(map);
while (iterator.hasNext())
System.out.println(
org.apache.commons.lang3.ArrayUtils.toString(iterator.next())
);
它会准确打印您的示例中指定的内容。
附:地图实际上是不需要的;它可以被一个简单的数组(或列表列表)替换。然后构造函数会变得更简单一些:
public CombinationsIterator(final String[][] array)
combinationLength = array.length;
values = array;
// ...
// Reorganize the map to array - THIS CAN BE REMOVED.
【讨论】:
非常感谢,亚历克斯。这段代码很棒。完美运行。以上是关于如何在 Java 中创建值组合?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 xml 和 kotlin 组合中创建类似布局的计算器
如何在 Spatstat 中创建带有覆盖两个标记的图例的组合颜色图?