使用 map().reduce() 初始化链表节点

Initialize a Linked List nodes using map().reduce()

我想使用 java 流对节点 class 进行初始化。我如何使用地图和减少流操作来执行此操作?

Node node5 = new Node(5,null);
Node node4 = new Node(4,node5);
Node node3 = new Node(3,node4);
Node node2 = new Node(2,node3);
Node node1 = new Node(1,node2);

我试过类似的方法,但无法编译

Node node = Arrays.stream(new int[]{1,2,3,4,5})
    .map((i, n) -> new Node(i, n)
    .reduce(null, new SingleList.Node(i, n)));

我想将数组中的每个元素映射到 Node(int i , Node n),然后将其缩减为 Node。我在这里错过了什么?

您可以使用 Stream API 和特别是 reduce() 操作生成链表。

但请记住,从本质上讲,这是模仿 for 循环的一种棘手方法(这将是一个更合适的选择),并且此流不能并行执行。

为了创建包含所需数量元素的流,无需生成数组然后将其用作 stream-source。相反,我们可以使用 IntStream.range(), which expects start and end int values (it will work only if start < end) or IntStream.iterate().

创建一个流

注意 需要 box() 操作才能将 IntStream 转换为对象流为了访问这种类型的 reduce(identity, accumulator, combiner),它允许提供与流中元素类型不同的类型标识。

这种形式的 reduce() 不适用于 IntStream

尾节点应作为reduce()操作的身份提供。 accumulator函数负责以相反的顺序(from tail to head)生成链表。作为流管道执行结果返回的节点将是头节点

Combiner 函数,用于组合并行生成的部分结果,抛出一个 AssertionError 因为如果我们允许并行执行,每个线程将实例化自己的身份(尾节点),这意味着节点链接不正确。

int tailValue = 5;
int headValue = 1;

// generating the linked list

Node head = IntStream.range(headValue, tailValue)
    .boxed()
    .reduce(new Node(tailValue, null),
            (Node node, Integer value) -> new Node(node.getValue() - 1, node),
            (Node left, Node right) -> {throw new AssertionError("should not be used in parallel");});

// printing the linked list

Node cur = head;
while (cur != null) {
    System.out.println(cur);
    cur = cur.getNext();
}

输出:

Node { value = 1 }
Node { value = 2 }
Node { value = 3 }
Node { value = 4 }
Node { value = 5 }

A link to the Online Demo

Tried something like this, which does not compile

Node node = Arrays.stream(new int[]{1,2,3,4,5})
            .map((i, n) -> new Node(i, n))
            .reduce(null, new SingleList.Node(i, n)));

Arrays.stream(new int[]{1,2,3,4,5}) 将生成 IntStream(不是 Stream)。

操作 map(),应用于 IntStream,需要一个类型为 IntUnaryOperator 的参数,它是一个接受类型为 int 和 returns int 还有

为了将基元流转换为对象流,您应该使用 mapToObj(),它需要一个 IntFunction(一个接受单个 int 参数和returns 一个对象)。

可用于 IntStreamreduce(int identity, IntBinaryOperator combiner) 形式需要基本类型 int 身份 及其 combinerIntBinaryOperator 类型,即它需要 int 并产生 int。因此,它不会编译。

您的解决方案中还有一个逻辑缺陷 - 您试图为每个蒸汽元素创建一个 节点 两次:第一次在 map() 内,然后在期间reduce() 操作,这显然是多余的。

根据文档 map() 是一个 stateless operation 它不应该“意识到”之前遇到的元素。这意味着它对生成 节点 没有帮助,因为它无法提供对 下一个节点 .

的引用

上面使用的虚拟 Node class:

public static class Node {
    private int value;
    private Node next;

    public Node(int value, Node next) {
        this.value = value;
        this.next = next;
    }

    public int getValue() {
        return value;
    }

    public Node getNext() {
        return next;
    }

    @Override
    public String toString() {
        return "Node {" +
            " value = " + value + " }";
    }
}

恕我直言,不需要流。只需创建一个所需长度的数组,使用 Arrays.setAll 方法填充数组并获取最后一个元素:

Node[] nodes = new Node[5];
Arrays.setAll(nodes, i -> new Node(nodes.length  - i , i == 0 ? null : nodes[i-1]));

Node node = nodes[nodes.length-1];

如果你想用数组的特定值而不是从 0 到 n 来初始化对象 NodeList,你可以使用 iterate 操作来生成索引并从最后一个元素到第一个元素遍历数组。然后,正如您所建议的,您可以将每个 int 映射到 Integer (或使用 boxed 操作),然后使用 reduce 操作从底部。

int[] vet = new int[]{7, 15, 6, 32, 44};
Node node = IntStream.iterate(vet.length - 1, i -> i >= 0, i -> i - 1)
        .mapToObj(i -> i)
        .reduce(null, (n, i) -> new Node(vet[i], n), (n1, n2) -> {
            throw new RuntimeException("No parallel execution supported");
        });

前面的代码引用了一个 Node 实现,如下所示:

class Node {
    private int info;
    private Node next;

    public Node(int info, Node next) {
        this.info = info;
        this.next = next;
    }

    //... getters & setters ...
}

输出

Node temp = node;
while (temp != null) {
    System.out.println(temp.getInfo());
    temp = temp.next;
}