Если вам не нужно жестко запрограммированное решение с простыми вложенными циклами, показанное в ответе Дариосицилии, вам потребуется хранить пары «замены-замены», например строку {url}
в паре со списком строк point1
и point2
. Простой класс может сделать это, как
class StringListPair{
public final String s;
public final List<String> l;
public StringListPair(String s,List<String> l){
this.s=s;
this.l=l;
}
}
, и затем список замен может быть инициализирован как
List<StringListPair> mappings=Arrays.asList(
new StringListPair("{url}",Arrays.asList("point1","point2")),
new StringListPair("{live}",Arrays.asList("10","20","30")));
(Если кто-то хочет полностью избежать использования вспомогательного класса, это все строки, так что List<List<String>>
тоже может выполнить эту работу, имея "{url}","point1","point2"
списки внутри, и тогда нам придется бороться с индексацией внутренних списков везде)
Тогда мне в голову приходят два общих подхода: рекурсивная, генерирующая все возможные комбинации за один прогон, и прямая индексация, нумерация всех комбинаций и генерация любой из них непосредственно по запросу. Рекурсию проще придумать, и у нее нет существенных недостатков, если все равно нужны все комбинации. Прямой подход генерирует одну комбинацию за раз, поэтому, если многие комбинации не будут использоваться, он может сэкономить много памяти и времени выполнения (например, если кому-то понадобится только одна случайно выбранная комбинация, возможно, из миллионов) .
Рекурсия будет, ну, в общем, рекурсивной, с завершенной комбинацией, созданной на самом глубоком уровне, поэтому ей необходимо следующее:
- список комбинаций (потому что он будет расширен глубоко внутри цепочки вызовов)
- сопоставления
- кандидата, над которым он работает в данный момент
- что-то, чтобы отследить, какую метку он должен заменить на данный момент.
Тогда остаются две вещи: рекурсия должна быть остановлена (когда больше не осталось меток для замены в текущем кандидате, она добавляется в список), или она должна заменить текущую метку чем-то, и переходите к следующему уровню.
В коде это может выглядеть так:
static void recursive(List<String> result,List<StringListPair> mappings,String sofar,int partindex) {
if(partindex>=mappings.size()) {
result.add(sofar);
return;
}
StringListPair p=mappings.get(partindex);
for(String item:p.l)
recursive(result,mappings,sofar.replace(p.s,item),partindex+1);
}
уровень отслеживается простым n Умбер, partindex
, текущий кандидат называется sofar
(от "до сих пор"). Когда индекс не ссылается на существующий элемент в mappings
, кандидат завершен. В противном случае он перебирает «текущее» отображение и вызывает себя при каждой замене, ну, рекурсивно.
Функция обертки для создания и возврата фактического списка:
static List<String> userecursive(List<StringListPair> mappings,String base){
List<String> result=new ArrayList<>();
recursive(result, mappings, base, 0);
return result;
}
Прямая индексация Вариант использует некоторые математические. У нас есть 2 * 3 комбинации в примере, пронумерованные от 0 ... 5. Если мы говорим, что эти числа построены из
i=0..1
и
j=0..2
, выражение для этого может быть
index=i+j*2
. Это можно изменить с помощью операций по модулю и делению, как для последнего индекса
index=5
:
i=5%2=1
,
j=5//2=2
. Где
%
- оператор по модулю, а
//
- целочисленное деление. Этот метод работает и с «большими размерами», только тогда он будет применять модуль по каждому шагу и обновлять сам индекс с делением, как это делает действительный код:
static String direct(List<StringListPair> mappings,String base,int index) {
for(StringListPair p:mappings) {
base=base.replace(p.s,p.l.get(index % p.l.size())); // modulo "trick" for current label
index /= p.l.size(); // integer division throws away processed label
}
return base;
}
Функция обертки (у нее есть все oop чтобы вычислить «2 * 3» в начале и собирать комбинации в список):
static List<String> usedirect(List<StringListPair> mappings,String base){
int total=1;
for(StringListPair p:mappings)
total*=p.l.size();
List<String> result=new ArrayList<>();
for(int i=0;i<total;i++)
result.add(direct(mappings,base,i));
return result;
}
Полный код и демонстрация включены Ideone