开发者

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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜