如何在Java中创建值的组合?

如何在Java中创建值的组合?,java,dictionary,hashmap,iterator,Java,Dictionary,Hashmap,Iterator,我有以下映射:map-map=newhashmap() 键是整数,值是数组(也可以用列表代替) 现在,我想得到键之间所有可能的值组合。例如,假设地图包含以下条目: key 1: "test1", "stackoverflow" key 2: "test2", "wow" key 3: "new" 这些组合包括 ("test1","test2","new") ("test1","wow","new") ("stackoverflow", "test2", "new") ("stackoverfl

我有以下映射:
map-map=newhashmap()

键是整数,值是数组(也可以用列表代替)

现在,我想得到键之间所有可能的值组合。例如,假设地图包含以下条目:

key 1: "test1", "stackoverflow"
key 2: "test2", "wow"
key 3: "new"
这些组合包括

("test1","test2","new")
("test1","wow","new")
("stackoverflow", "test2", "new")
("stackoverflow", "wow", "new")
为此,我设想了一个方法
boolean hasNext()
,如果有下一对,它将返回true;另一个方法仅返回下一组值(如果有)

如何做到这一点?地图也可以被其他数据结构取代。

该算法基本上与十进制数的增量算法(“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");
    }

}

我认为这是一个挑战,看看新的Java8API是否有助于解决此类问题。下面是我对这个问题的解决方案:

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;
    }
}
输出将是:

[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]

这可以通过递归实现,但如何。。。这仍然是一个有待回答的问题……不:)您可以轻松地完成这项工作,而无需递归。算上一个“变量”基数。非常感谢你,亚历克斯。这段代码太棒了。工作完美。
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;
    }
}
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]