列出连接列表的视图

时间:2017-09-08 15:15:22

标签: java collections view lazy-evaluation

我想创建一个列表视图,其中包含许多其他列表的串联。

示例:

List<Integer> l1 = Lists.newArrayList(1);
List<Integer> l2 = Lists.newArrayList(2);
List<Integer> l3 = Lists.newArrayList(3);

// This call should not copy any of the argument lists content
List<Integer> concatenation = new ConcatenationListView(l1, l2, l3);

System.out.println(concatenation); // Prints [1, 2, 3]
l3.add(4);
System.out.println(concatenation); // Prints [1, 2, 3, 4]

我可以在Java中使用哪种技术和/或模式来满足这些要求?

详细信息

  • 我希望连接是懒惰的。 (在这种情况下,这就是“观点”的含义。)
  • 我希望更改组件列表对连接的反映。 (也是“观点”的意思)。
  • 视图无需修改。
  • 这与List.subList方法相反。
  • 它类似于Guava Iterables.concat方法所做的,但我需要一个列表作为结果,而不是可迭代的。
  • 我的问题类似于this one,但我需要List作为结果连接,其他问题的海报内容为Iterable

3 个答案:

答案 0 :(得分:1)

Java类库没有这样的组合类,因此您需要自己编写代码。

由于您希望类允许修改基础列表,因此效率不会高于O(L),其中L是视图管理的列表数,因为索引转换需要您每次需要读取或插入特定索引时,都要遍历列表列表。

另一方面,这使得实现变得非常简单 - 翻译索引的代码如下所示:

class ListView : List<T> {
    private List<List<T>> lists = ...
    public T get(int index) {
        int i = 0;
        while (i != lists.size() && index > lists.get(i).size()) {
            index -= lists.get(i++).size();
        }
        if (i == lists.size()) {
            throw new IndexOutOfBoundsException();
        }
        return lists.get(i).get(index);
    }
    ...
}

答案 1 :(得分:0)

以下是一个类的实现,它充当多个列表串联的视图。

它没有采取多行措施:

/**
 * A list which acts as view of the concatenation of a number of lists.
 */
public class ListConcatenationView<E> extends AbstractList<E> {
    private final List<E>[] lists;

    @SafeVarargs
    public ListConcatenationView(List<E>... lists) {
        this.lists = lists;
    }

    @Override
    public E get(int ix) {
        int localIx = ix;
        for (List<E> l : lists) {
            if (localIx < 0) throw new IndexOutOfBoundsException(ix); 
            if (localIx < l.size()) return l.get(localIx);
            localIx -= l.size();
        }
        return null;
    }

    @Override
    public int size() {
        int size = 0;
        for (List<E> l : lists) {
            size += l.size();
        }
        return size;
    }
}

答案 2 :(得分:-1)

你可以使用Java8 Stream-API作为这个。

Stream.of(l1, l2, l3)
    .flatMap(List::stream)
    .forEach(System.out::println);

这将创建三个列表的Stream,然后通过将每个列表转换为流本身(List::stream)并将它们平面映射在一起来将每个列表中的所有项连接在一起。毕竟它输出控制台上的每一个元素。

Plus:默认情况下Streams是懒惰的,这意味着

  

源数据的计算仅在启动终端操作时执行,源元素仅在需要时使用。

Source