首页 文章

在Java 8中将令牌流映射到n-gram流

提问于
浏览
4

我认为这是一个关于Java 8流的一个相当基本的问题,但我很难想到正确的搜索术语 . 所以我在这里问 . 我刚刚进入Java 8,所以请耐心等待 .

我想知道如何将标记流映射到n-gram流(表示为大小为n的标记数组) . 假设n = 3,那么我想转换下面的流

{1, 2, 3, 4, 5, 6, 7}

{[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]}

如何使用Java 8流实现此目的?应该可以同时计算这个,这就是为什么我有兴趣用流来实现这个目标(处理n阵列的顺序也无关紧要) .

当然,我可以使用老式for循环轻松完成,但我更喜欢使用流API .

2 回答

  • 3

    这样的操作不适合Stream API . 在功能术语中,您要做的事情被称为大小为 n 的滑动窗口 . Scala内置了 sliding() 方法,但Java Stream API中没有任何内置功能 .

    您必须依赖于在输入列表的索引上使用Stream来实现这一点 .

    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        List<List<Integer>> result = nGrams(list, 3);
        System.out.println(result);
    }
    
    private static <T> List<List<T>> nGrams(List<T> list, int n) {
        return IntStream.range(0, list.size() - n + 1)
                        .mapToObj(i -> new ArrayList<>(list.subList(i, i + n)))
                        .collect(Collectors.toList());
    }
    

    此代码只是在输入列表的索引上生成一个Stream,将它们中的每一个映射到一个新列表,该列表是从 ii+n (排除)获取列表值的结果,并将所有这些值收集到List中 .

  • 4

    如果您没有随机访问源数据,则可以使用自定义收集器完成此操作:

    List<Integer> data = Arrays.asList(1,2,3,4,5,6,7);
    
    List<List<Integer>> result = data.stream().collect(window(3, toList(), toList()));
    

    这是 window 的来源 . 它是平行友好的:

    public static <T, I, A, R> Collector<T, ?, R> window(int windowSize, Collector<T, ?, ? extends I> inner, Collector<I, A, R> outer) {
    
        class Window {
            final List<T> left = new ArrayList<>(windowSize - 1);
            A mid = outer.supplier().get();
            Deque<T> right = new ArrayDeque<>(windowSize);
    
            void add(T t) {
                right.addLast(t);
                if (left.size() == windowSize - 1) {
                    outer.accumulator().accept(mid, right.stream().collect(inner));
                    right.removeFirst();
                } else {
                    left.add(t);
                }
            }
    
            Window merge(Window other) {
                other.left.forEach(this::add);
                if (other.left.size() == windowSize - 1) { 
                    this.mid = outer.combiner().apply(mid, other.mid);
                    this.right = other.right;
                }
                return this;
            }
    
            R finish() {
                return outer.finisher().apply(mid);
            }
        }
    
        return Collector.of(Window::new, Window::add, Window::merge, Window::finish);
    }
    

相关问题