Создайте поток пользовательских переменных чисел

Как я могу сделать 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
  }
}