How rotate non consecutive elements in a list
I have a List containing elements that are accessed according to their indexes. In this list I 开发者_如何学编程need to be able to "rotate" groups of 4 elements according to their index. For example in the list
[a, b, c, d, e ,f , g, h, i, j, k, l]
I want to rotate c, f, i, l in order to get
[a, b, l, d, e ,c , g, h, f, j, k, i]
How will you implement this ?
A straightforward solution
If you only need to 1-rotate elements at 4 indices of a List
, you can just write a straightforward generic method like this:
static <T> void rotate4(List<T> list, int i0, int i1, int i2, int i3) {
T item = list.get(i3);
item = list.set(i0, item);
item = list.set(i1, item);
item = list.set(i2, item);
item = list.set(i3, item);
}
This will cyclically rotate 4 elements of any List<T>
. Remember that List.set
returns the element that previously was at that index, so you could write the entire method in one-line if you want:
// one-liner version
list.set(i3, list.set(i2, list.set(i1, list.set(i0, list.get(i3)))));
With this helper method, you'll have:
List<Character> list = Arrays.asList(
'a','b','c','d','e','f','g','h','i','j','k','l'
);
System.out.println(list);
// [a, b, c, d, e, f, g, h, i, j, k, l]
// * * * *
rotate4(list, 2, 5, 8, 11);
System.out.println(list);
// [a, b, l, d, e, c, g, h, f, j, k, i]
// * * * *
A more general solution
IF you need a way to rotate an arbitrary number of elements for an arbitrary distance, then you can create a live view of another List
, and then you can Collections.rotate
that view.
IF the elements are consecutive, for example, you'd just use subList
:
List<Character> list = Arrays.asList(
'a','b','c','d','e','f','g','h','i','j','k','l'
);
System.out.println(list);
// [a, b, c, d, e, f, g, h, i, j, k, l]
// * * * * *
System.out.println(list.subList(1, 6));
// [b, c, d, e, f]
Collections.rotate(list.subList(1, 6), -2);
System.out.println(list);
// [a, d, e, f, b, c, g, h, i, j, k, l]
// * * * * *
Since the elements aren't consecutive, you can't use subList
, but you can write e.g. PeriodicalLiveViewList
class. You want to be able to write something like this:
System.out.println(PeriodicalLiveViewList.of(list, 3, 2));
// [c, f, i, l]
Collections.rotate(PeriodicalLiveViewList.of(list, 3, 2), 1);
Basically you create another List
whose elements are every 3rd element of another List
, starting at index 2, as a live view.
If you are using Guava, there is ForwardingList
that you can built on. You can implement the decorator pattern for this from scratch too if necessary.
Related questions
- Guava ForwardingList usage example
Here's what I came up with. It's a rather simple brute-force solution, but it's extensible.
import java.util.Arrays;
import java.util.List;
public class ArrayRotator {
public static void main(String... args) {
List<String> target = Arrays.asList("a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l");
System.out.println(target);
target = rotate(target, 3);
System.out.println(target);
}
private static List<String> rotate(List<String> aList, int anOffset) {
String[] result = new String[aList.size()];
for (int i = 0; i < aList.size(); i++) {
if (0 == (i + 1) % anOffset) {
if (aList.size() > (i + anOffset)) {
result[i + anOffset ] = aList.get(i);
} else {
result[anOffset - 1] = aList.get(i);
}
} else {
result[i] = aList.get(i);
}
}
return Arrays.asList(result);
}
}
And here's the output I got:
[a, b, c, d, e, f, g, h, i, j, k, l]
[a, b, l, d, e, c, g, h, f, j, k, i]
I didn't think about the Collections
functionality in this case, as it seemed to be a bit more complex than that. I'm not sure which algorithm would be more efficient on large lists. Maybe you could combine the functionality as below:
import java.util.List;
public class ListRotator {
public static void main(String... args) {
List<String> target = Arrays.asList("a", "b", "c", "d", "e", "f", "g",
"h", "i", "j", "k", "l");
System.out.println(target);
rotate(target, 3);
System.out.println(target);
}
private static void rotate(List<String> aList, int anOffset) {
for (int i = anOffset-1; i < aList.size()-anOffset; i += anOffset) {
Collections.rotate(aList.subList(i, i+anOffset), -1);
}
Collections.rotate(aList.subList(anOffset-1, aList.size()), 1);
}
}
It generates the same output as above.
精彩评论