将标记流映射到 Java 8 中的 n-gram 流
Mapping a stream of tokens to a stream of n-grams in Java 8
我认为这是一个关于 Java 8 个流的相当基本的问题,但我很难想到正确的搜索词。所以我在这里问。我刚刚进入 Java 8,请耐心等待。
我想知道如何将标记流映射到 n-gram 流(表示为大小为 n 的标记数组)。假设n = 3,那么我想转换成下面的stream
{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.
这样的操作不太适合 Stream API。在功能术语中,您尝试做的事情称为大小 n
的滑动 window。 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,将它们中的每一个映射到一个新列表,该列表是从 i
到 i+n
获取列表值的结果(排除)并将所有这些收集到一个列表中。
如果您不能随机访问源数据,您可以使用自定义收集器完成此操作:
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);
}
基于
以下应该有效
int n = 3;
List<Integer> intList = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
IntStream.rangeClosed(0, intList.size() - n)
.mapToObj(i -> intList.subList(i, i+n))
.collect(Collectors.toList());
我认为这是一个关于 Java 8 个流的相当基本的问题,但我很难想到正确的搜索词。所以我在这里问。我刚刚进入 Java 8,请耐心等待。
我想知道如何将标记流映射到 n-gram 流(表示为大小为 n 的标记数组)。假设n = 3,那么我想转换成下面的stream
{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.
这样的操作不太适合 Stream API。在功能术语中,您尝试做的事情称为大小 n
的滑动 window。 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,将它们中的每一个映射到一个新列表,该列表是从 i
到 i+n
获取列表值的结果(排除)并将所有这些收集到一个列表中。
如果您不能随机访问源数据,您可以使用自定义收集器完成此操作:
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);
}
基于
以下应该有效
int n = 3;
List<Integer> intList = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
IntStream.rangeClosed(0, intList.size() - n)
.mapToObj(i -> intList.subList(i, i+n))
.collect(Collectors.toList());