Подтвердить что ты не робот

Как использовать Java Collections.shuffle() в массиве Scala?

У меня есть массив, который я хочу перестановочить случайным образом. В Java существует метод Collections.shuffle(), который может случайным образом перемешать элементы списка. Его также можно использовать в массиве:

String[] array = new String[]{"a", "b", "c"};

// Shuffle the array; works because the list returned by Arrays.asList() is backed by the array
Collections.shuffle(Arrays.asList(array));

Я попытался использовать это в массиве Scala, но интерпретатор Scala отвечает длинным ответом:

scala> val a = Array("a", "b", "c")
a: Array[java.lang.String] = Array(a, b, c)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a))
<console>:6: warning: I'm seeing an array passed into a Java vararg.
I assume that the elements of this array should be passed as individual arguments to the vararg.
Therefore I follow the array with a `: _*', to mark it as a vararg argument.
If that not what you want, compile this file with option -Xno-varargs-conversion.
       java.util.Collections.shuffle(java.util.Arrays.asList(a))
                                                             ^
<console>:6: error: type mismatch;
 found   : Array[java.lang.String]
 required: Seq[Array[java.lang.String]]
       java.util.Collections.shuffle(java.util.Arrays.asList(a))
                                                             ^

Что именно здесь происходит? Я не хочу компилировать свой код со специальным флагом (-Xno-varargs-conversion), если это решение вообще, только из-за этого.

Итак, как я могу использовать Java Collections.shuffle() в массиве Scala?

Я написал свой собственный метод тасования в Scala тем временем:

// Fisher-Yates shuffle, see: http://en.wikipedia.org/wiki/Fisher–Yates_shuffle
def shuffle[T](array: Array[T]): Array[T] = {
    val rnd = new java.util.Random
    for (n <- Iterator.range(array.length - 1, 0, -1)) {
        val k = rnd.nextInt(n + 1)
        val t = array(k); array(k) = array(n); array(n) = t
    }
    return array
}

Он перемещает массив на место и возвращает массив для удобства.

4b9b3361

Ответ 1

java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

Для правильной работы вышеизложенного тип элемента должен быть подклассом scala.AnyRef(эквивалентно java.lang.Object), поскольку Arrays.asList() использует массив, переданный в качестве хранилища резервных копий для result java.util.List и java.util.List могут содержать только ссылки на объекты (не примитивные значения). *

Вот почему Collections.shuffle(), который перетасовывает переданный java.util.List, фактически перетасовывает массив. *

*: см. примечание ниже

Например:

scala> val a = Array[java.lang.Integer](1, 2, 3) // note the type parameter                  
a: Array[java.lang.Integer] = Array(1, 2, 3)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

scala> a
res43: Array[java.lang.Integer] = Array(1, 3, 2)

scala> java.util.Collections.shuffle(java.util.Arrays.asList(a:_*))

scala> a
res45: Array[java.lang.Integer] = Array(1, 2, 3)

Примечание: для приведенных выше фрагментов кода используется Scala 2.7.5. Scala 2.8.0 демонстрирует различное поведение, как продемонстрировал Даниэль. Чтобы быть в безопасности, не зависеть от того, что массив перемещается, но вместо этого список, возвращаемый из массива Arrays.asList(), перетасовывается.

scala> val a = Array[java.lang.Integer](1, 2, 3)
a: Array[java.lang.Integer] = Array(1, 2, 3)

scala> val b = java.util.Arrays.asList(a:_*)
b: java.util.List[java.lang.Integer] = [1, 2, 3]

scala> java.util.Collections.shuffle(b)

scala> b
res50: java.util.List[java.lang.Integer] = [2, 1, 3]

scala> java.util.Collections.shuffle(b)

scala> b
res52: java.util.List[java.lang.Integer] = [3, 1, 2]

Ответ 2

Кажется, что Scala делает что-то отличное от Java, когда речь заходит о varargs. По крайней мере, я не могу заставить этот массив перемещаться так, как я пытаюсь. Предположительно, перетасовка в списке будет перемешать массив, потому что список поддерживается массивом. Похоже, что Scala создаст новый массив при передаче vararg аргументов в Java, поэтому делает вышеупомянутый пример бесполезным.

scala> val b = java.util.Arrays.asList(a: _*)
b: java.util.List[java.lang.String] = [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [c, b, a]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, c, b]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [b, a, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [a, b, c]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(a, b, c) [c, a, b]

Он работает с Интами, несмотря на требование иначе:

scala> val a = Array(1,2,3)
a: Array[Int] = Array(1, 2, 3)

scala> val b = java.util.Arrays.asList(a: _*)
b: java.util.List[Int] = [1, 2, 3]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [2, 3, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [3, 2, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [3, 2, 1]

scala> java.util.Collections.shuffle(b); println(a.toString+" "+b.toString)
Array(1, 2, 3) [1, 2, 3]

В Scala 2.8 существует более простой способ:

scala> scala.util.Random.shuffle(a)
res32: Sequence[Int] = Array(1, 2, 3)

scala> scala.util.Random.shuffle(a)
res33: Sequence[Int] = Array(2, 1, 3)

scala> scala.util.Random.shuffle(a)
res34: Sequence[Int] = Array(3, 2, 1)

Обратите внимание, что в режиме Scala он не изменяет исходный массив.

Ответ 3

Чтобы ответить на вопрос "что именно происходит здесь?" часть:

Когда вы говорите, что java.util.Arrays.asList(a) вы вызываете статический метод Java, который определен для принятия переменного количества аргументов (синтаксис vararg... в Java):

public static <T> List<T> asList(T... a) 

В Scala, когда вы делаете вызов asList, вы передаете один массив Array [String], а не три строковых параметра. Даже если Scala представляет T * как Array [T], вам нужно сообщить компилятору, что вы действительно хотите передать три параметра, а не один параметр, который является списком из трех вещей.

Scala имеет удобный способ преобразования вашего массива [String] в String, String, String: вы используете символ _ *, как показано в ответе Уолтера Чанга. Вы можете использовать его всякий раз, когда передаете что-то в функцию vararg.

Это описано на страницах 188 и 189 программирования в Scala.

Вы также увидите _ * в сопоставлении с образцом, чтобы соответствовать нулю или более элементам в списке.