Я хочу иметь Map
в форме:
0 -> 1
2 -> 0
1 -> 2
Или
0 -> 4
1 -> 0
2 -> 2
3 -> 3
4 -> 1
Предполагается, что каждый int будет отображаться точно в отдельный int в диапазоне. Я хочу передать функцию int
размером карты и вернуть объект Map
. Какой хороший способ реализовать эту функциональность?
public static Map<Integer,Integer> bijectiveMap(int n) {
List<Integer> values = new ArrayList<>(n);
for (int i=0 ; i<n ; i++)
values.add(i);
Collections.shuffle(values);
Map<Integer,Integer> result = new HashMap<>();
for (int i=0 ; i<n ; i++)
result.put(i,values.get(i));
return result;
}
Выход для n = 10:
{0=7, 1=5, 2=3, 3=6, 4=1, 5=4, 6=2, 7=8, 8=9, 9=0}
Может произойти тасовка. Возможно, это не самый быстрый, но, безусловно, самый чистый способ.
public Map<Integer, Integer> getRandomMapping(int min, int max){
List<Integer> arr = new ArrayList<Integer>();
//Fill array in order
for(int i = 0; i < max + 1; i++){
arr.add(i + min);
}
//Shuffle
Collections.shuffle(arr);
//Read into map
Map<Integer, Integer> m = new HashMap<Integer, Integer>();
for(int i = 0; i < max + 1; i++){
m.put(new Integer(i + min), arr.get(i));
}
return m;
}
Забудьте о Map
. Просто перетасуйте Array
ArrayList
(инициализированный с a[i] = i
). Качество распределения зависит от вашего алгоритма тасования.
ArrayList
работать проще. Если по какой-то причине действительно необходимо вернуть Map
, вы можете создать класс, который оборачивает массив и реализует интерфейс Map
.
Если избегайте Collections.shuffle. Вот
public static Map<Integer, Integer> foo(final int size) {
final Map<Integer, Integer> out = new HashMap<Integer, Integer>(size*5);
final List<Integer> values = new ArrayList<Integer>(size);
Random r = new Random();
for (int i = 0; i < size; i++) {
values.add(i, i);
}
for (int i = size; i > 0; i--) {
out.put(new Integer(i), values.remove(r.nextInt(i)));
}
return out;
}
Использовать коллекции Apache для int и long, а не для Integer wrapper
final static transient Random r = new Random();
final public static Map<Integer, Integer> foo(final int size) {
final Map<Integer, Integer> out = new HashMap<Integer, Integer>(size*5);
final List<Integer> values = new ArrayList<Integer>(size);
int i;
for (i = 0; i < size; i++) {
values.add(i, i);
}
for (; i > 0; i--) {
out.put(new Integer(i), values.remove(r.nextInt(i)));
}
return out;
}
Я прошу прощения за циклы List, это было слишком много std :: vector на сегодняшний день. Быстрое решение
final static transient Random rand = new Random();
final public static Map<Integer, Integer> foo(final int size) {
final Map<Integer, Integer> out = new HashMap<Integer, Integer>(size*5);
final Integer[] values = new Integer[size];
int i, r;
for (i = 0; i <size; i++) {
values[i] = i;
}
for (; i > 0; i--) {
r = rand.nextInt(i);
out.put(i, values[r]);
values[r] = values[i-1];
}
return out;
}
values
имеет ужасную стоимость, тогда как Collections.shuffle меняет только элементы. Так или иначе !
The most efficient way to implement this functionality would be to not use Java.
Почему это ?