如何创建一个从给定顺序范围中间开始的IntStream
,然后从中间开始流式传输下一个数字,并交替向左和向右。例如,对于给定的连续数字范围1 2 3 4 5
,自定义序列将为3 2 4 1 5
或3 4 2 5 1
,具体取决于您是先从左侧还是右侧开始。
我基本上试图从中间开始迭代一个数组并向外均匀地向外(不要先完全向左或向右)。
我使用for
循环尝试了这个但是代码很麻烦,我认为只需排列一个集合或数字流而不是检查它就会多更好在运行中(因为必须检查的所有索引越界异常)。这是原始代码,我认为作为预先计算的整数流更好:
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
}
}
答案 0 :(得分:8)
试试这个:
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)
表达式的作用。然后,我们除以2以便从中点获得偏移量:
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)
来终止序列。
答案 1 :(得分:2)
好吧,我能想到这一点,不知道它是否符合你的要求:
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);
}
答案 2 :(得分:2)
此解决方案使用迭代器和流:
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
答案 3 :(得分:2)
既然你说过,你想用序列迭代一个数组,我把它改为生成包括零的数字,但不包括 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’ answer,但使用IntStream.rangeClosed()
作为基础,创建大小的流,其效果比创建无限流更有效(如iterate
)并应用limit
,尤其适用于toArray
,count
和parallel
流等操作。换句话说,性能与按通常顺序迭代范围/数组相同。
通过使用最低位作为符号来转换自然数范围,该符号对于递增的数字是交替的,并且将数字向右移位一位,这相当于将数字的大小除以2。
每个元素只执行一次比特移位的替代符号
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));
}
答案 4 :(得分:1)
我们可以自定义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]
答案 5 :(得分:1)
由于您愿意考虑一个集合,以下&#34;低技术&#34;方法很简单:
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)
答案 6 :(得分:1)
使用公式为从开始到结束的任何顺序范围生成流
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);
}