Создайте поток пользовательских переменных чисел
Как я могу сделать IntStream
, который начинается в середине заданного последовательного диапазона, а затем передает следующие числа, начиная с середины и чередуя их влево и вправо. Например, для данного последовательного диапазона чисел 1 2 3 4 5
пользовательская последовательность будет 3 2 4 1 5
или 3 4 2 5 1
в зависимости от того, начинаете ли вы сначала влево или вправо.
Я в основном пытаюсь выполнить итерацию массива, начиная с середины и равномерно выходящего наружу (сначала не влево или вправо).
Я пробовал это, используя только циклы for
, но код беспорядочен, и я думаю, что было бы лучше просто выстроить коллекцию или поток чисел вместо того, чтобы проверять ее на летать (из-за всех исключений из списка исключений, которые необходимо проверить). Вот исходный код, который, я думаю, будет намного лучше, чем предварительно вычисленный поток ints:
int middle = myArray.length / 2;
Object value = myArray[middle]; //have to reference middle since the for loop won't
//do operation on value
for (int offset = 1; true; offset++) {
int nextRight = middle + offset;
int nextLeft = middle - offset;
if (nextRight < myArray.length) { // Have to guard against exception, but can't catch exception ahead of time because left or null may not be empty.
Object value = myArray[nextRight];
//do operation on value
}
if (nextLeft >= 0) {
Object value = myArray[nextRight];
//do operation on value
}
if (nextRight >= myArray.length) {
break; //exit logic
}
if (nextLeft < 0) {
break; //exit logic
}
}
Ответы
Ответ 1
В этом решении используются итераторы и потоки:
boolean toLeft = false;
int size = 5;
int half = size % 2 == 0 ? size / 2 : size / 2 + 1;
IntStream inferiorStream = IntStream.iterate (half, x -> x - 1);
IntStream superiorStream = IntStream.iterate (half, x -> x + 1);
OfInt a = toLeft
? inferiorStream.iterator ()
: superiorStream.iterator ();
OfInt b = toLeft
? superiorStream.skip (1).iterator ()
: inferiorStream.skip (1).iterator ();
IntStream stream = Stream.generate (() -> IntStream.concat (
a.hasNext () ? IntStream.of (a.nextInt ()) : IntStream.empty (),
b.hasNext () ? IntStream.of (b.nextInt ()) : IntStream.empty ()))
.flatMapToInt (Function.identity ())
.limit (size);
stream.forEach (System.out :: println);
Выход (toLeft = true):
3
4
2
5
1
Выход (toLeft = false):
3
2
4
1
5
Ответ 2
Попробуйте следующее:
import static java.lang.Integer.signum;
static IntStream altSeq(int n) {
return IntStream.iterate(2 * (n % 2) - 1, i -> -i - signum(i))
.map(i -> i / 2 + (n + 1) / 2)
.limit(n);
}
Например, altSeq(5)
производит:
[3, 2, 4, 1, 5]
и запуск altSeq(6)
вызывает:
[3, 4, 2, 5, 1, 6]
Вкратце, мы генерируем восходящую последовательность, которая чередует знак:
1, -2, 3, -4, 5, ...
Это то, что делает выражение i -> -i - signum(i)
. Затем мы делим на два, чтобы получить смещения от середины:
0, -1, 1, -2, 2, ...
То, откуда происходит i / 2
в первом члене выражения map
. Затем добавим середину диапазона 1..n, которая является (n + 1) / 2
, вторым членом выражения map
. При n = 5 это дает нам
3, 2, 4, 1, 5, ...
Начиная с 1 работает для последовательности нечетной длины. Для четных длин мы хотим начать с -1. Серийное выражение 2 * (n % 2) - 1
вычисляет правое начальное значение как для четных, так и для нечетных последовательностей длины.
Наконец, применяем limit(n)
для завершения последовательности.
Ответ 3
Ну, я могу подумать об этом, не знаю, соответствует ли это вашим требованиям:
public static IntStream generate(int[] x, boolean direction) {
int length = x.length / 2;
IntStream right = IntStream.range(1, length + 1)
.mapToObj(i -> {
return direction ? new Integer[] { i, -1 * i } : new Integer[] { -1 * i, i };
})
.flatMap(Arrays::stream)
.mapToInt(i -> x[length + i]);
return IntStream.concat(IntStream.of(x[length]), right);
}
Ответ 4
Поскольку вы сказали, что хотите использовать последовательность для итерации по массиву, я изменил ее, чтобы произвести числа, включая ноль, но исключая n, поэтому вы можете напрямую передать длину массива и получить допустимые индексы.
Затем вы можете использовать
static IntStream altSeq(int n) {
int mid = (n-1)/2;
return IntStream.rangeClosed(1, n)
.map(i -> mid + (i>>>1)*signum(rotateRight(i,1)));
}
Этот подход похож на ответ Stuart Marks, но использует базу IntStream.rangeClosed()
в качестве базы, которая создает размерный поток, который работает намного эффективнее, чем создание бесконечного (например, iterate
) и применяя limit
, особенно для операций типа toArray
, count
и для потоков parallel
. Другими словами, производительность наравне с итерированием по диапазону/массиву в обычном порядке.
Диапазон натуральных чисел преобразуется с использованием младшего бита в качестве знака, который чередуется для восходящих чисел и сдвигает числа на один бит вправо, что эквивалентно делению величины чисел на два.
Альтернативная нотация, выполняющая только один бит-бит на элемент, будет
static IntStream altSeq(int n) {
int mid = (n-1)/2;
return IntStream.rangeClosed(1, n)
.map(i -> Integer.rotateRight(i, 1))
.map(i -> mid + (i&Integer.MAX_VALUE)*signum(i));
}
Ответ 5
мы можем настроить метод IntStream.generate
для генерации IntStream
в нужной последовательности
public void run(String... args) throws Exception {
final int[] arr = IntStream.range(0, 9).toArray(); //test data
int mid = arr.length / 2;
SeqGen seq = new SeqGen(arr, mid);
List<Integer> ints = IntStream.generate(() -> seq.gen())
.limit(arr.length)
.boxed()
.collect(Collectors.toList());
System.out.println(ints);
}
SeqGen
private class SeqGen {
int[] arr;
int start;
int curr;
boolean flag;
public SeqGen(int[] arr, int start) {
this.arr = arr;
this.start = start;
}
public int gen() {
int ret = -1;
int l = arr[start + curr];
int r = arr[arr.length - curr - start - 1];
if (!flag) {
ret = l;
curr++;
} else {
ret = r;
}
flag = !flag;
return ret;
}
}
Выход
[4, 3, 5, 2, 6, 1, 7, 0, 8]
Ответ 6
Используемая формула для генерации потока для любого последовательного диапазона от начала до конца
public static IntStream shuffle(int start, int end){
int size = end - start + 1;
int center = start + ((size + 1) >> 1) - 1;
int even = (size + 1) & 1;
int direction = 1 - (even << 1); //for left first: (even << 1) - 1;
return IntStream.range(1, size + 1)
.map(i -> center + (even + i * direction * ((((i + 1) & 1) << 1) - 1)) / 2);
}
Ответ 7
Поскольку вы готовы рассмотреть коллекцию, следующий "низкотехнологичный" подход довольно прост:
public static List<Integer> f(int len) {
int offset = len / 2;
ArrayList<Integer> indices = new ArrayList<>(len);
for(int i = 0 ; i < len; i++) {
int index = offset + i * direction(i);
indices.add(index);
offset = index;
}
return indices;
}
private static int direction(int size) {
return (size & 1) == 0 ? 1 : -1;
}
Вызов f(5),
возвращает: [2, 1, 3, 0, 4]
Направление (слева направо или справа налево) можно изменить, изменив direction()
. Если вам действительно нужны индексы на основе 1, измените f()
, чтобы иметь: indices.add(index+1)
Ответ 8
class Test
{
static IntStream alternateNumbers(int start, boolean incrementFirst)
{
return IntStream.generate(new IntSupplier()
{
boolean dec = !incrementFirst;
int left = dec ? start - 1 : start;
int right = left + 1;
@Override
public int getAsInt()
{
dec = !dec;
return dec ? left-- : right++;
}
});
}
public static void main(String[] args) throws Exception
{
alternateNumbers(10, true)
.limit(6)
.forEach(n -> System.out.printf("%02d ", n));
// 10 11 09 12 08 13
System.out.println();
alternateNumbers(10, false)
.limit(6)
.forEach(n -> System.out.printf("%02d ", n));
// 10 09 11 08 12 07
}
}