Home > Software engineering >  Initialize a Linked List nodes using map().reduce()
Initialize a Linked List nodes using map().reduce()

Time:05-26

I want to do this initialization of the Node class using java streams . How do I do this using map and reduce using java streams ?

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);

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)));

I want to map each element in the array to Node(int i , Node n) and then reduce it to a Node. What am I missing here?

CodePudding user response:

IMHO, no need for streams. Just create an array of desired length, use the Arrays.setAll method to fill the array and get the last element:

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];

CodePudding user response:

You can generate a Linked List using Stream API and specifically reduce() operation.

But keep in mind that in essence it's a tricky way to mimic the for loop (which will be a more suitable option) and this stream can't be executed in parallel.

In order to create a stream containing the required number of elements, there's no need for generating an array and then use it as a stream-source. Instead, we can create a stream by using either IntStream.range(), which expects start and end int values (it will work only if start < end) or IntStream.iterate().

Note that box() operation in required in order to transform the IntStream into a stream of objects in order to get access to this flavor of reduce(identity, accumulator, combiner), wich allows to provide the identity of the type that differs from the type of the elements in the stream.

This form of reduce() is not available with IntStream.

The tail node should be provided as the identity of reduce() operation. The accumulator function is responsible for generating the linked list in reversed order (from tail to head). The node returned as a result of the execution of the stream pipeline would be the head node.

Combiner function, which is used to combine partial results produces in parallel, throws an AssertionError because if we would allow parallel execution each thread will instantiate its own identity (the tail node), which means that nodes would be linked incorrectly.

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();
}

Output:

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}) will produce an IntStream (not a Stream).

Operation map(), applied on the IntStream, expects an argument of type IntUnaryOperator, which is a function that takes a single argument of type int and returns int as well.

In order to transform the stream of primitives into a stream of objects, you should use mapToObj(), which expects an IntFunction (a function that takes a single int argument and returns an object).

The form of reduce(int identity, IntBinaryOperator combiner), which is available with IntStream expects the identity of primitive type int, and its combiner is of type IntBinaryOperator, i.e. it takes int and produces int. Therefore, it would not compile.

There is also a logical flaw in your solution - you are attempting to create a node for each steam element twice: first time inside the map() and than during the reduce() operation, which is clearly redundant.

According to the documentation map() is a stateless operation it should not be "aware" of the previously encountered elements. Which means it can't be helpful in generating nodes, because it's unable to provide the reference to the next node.


A dummy Node class used above:

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   " }";
    }
}
  • Related