12

I have a Queue<Integer> declared as Queue<Integer> queue=new LinkedList();, I need to reverse the elments order in it, and then convert it into an int array. I wrote below code:

Collections.reverse((List)queue);
int[] res=queue.stream().mapToInt(Integer::intValue).toArray();

This code has two problems:

  1. the explict casting (List)queue;
  2. I wonder if there is a one line solution.

So do we have any more elegant way to do this?


Clearification of the problem:

Whether the queue is reversed is not important. An int array of the reversed elements is what I need.

Salem
  • 13,516
  • 4
  • 51
  • 70
ZhaoGang
  • 4,491
  • 1
  • 27
  • 39

9 Answers9

8

First, please don't use raw types (do use the diamond operator). Not quite a one liner, but you could first convert to an int[] and then use commons lang ArrayUtils.reverse(int[]) like

Queue<Integer> queue = new LinkedList<>();
// ...
int[] arr = queue.stream().mapToInt(Integer::intValue).toArray();
ArrayUtils.reverse(arr);

You could also write your own int[] reverse method that allowed for a fluent interface (e.g. return the int[]) then you could make it a one liner. Like,

public static int[] reverse(int[] arr) {
    for (int i = 0; i < arr.length / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[arr.length - i - 1];
        arr[arr.length - i - 1] = temp;
    }
    return arr;
}

And then

int[] arr = reverse(queue.stream().mapToInt(Integer::intValue).toArray());
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249
  • but this wouldn't reverse the queue. – Naman Jan 04 '19 at 01:39
  • 2
    @nullpointer True. But, if the goal is a reversed `int[]` then it isn't clear that the queue must also be reversed. In fact, I would assume the queue goes out of scope and the `int[]` is returned to the caller. – Elliott Frisch Jan 04 '19 at 01:42
8

No need to get fancy here.

static int[] toReversedArray(Queue<Integer> queue) {
    int i = queue.size();
    int[] array = new int[i];
    for (int element : queue) {
        array[--i] = element;
    }
    return array;
}

Not a one-liner, but easy to read and fast.

xehpuk
  • 7,814
  • 3
  • 30
  • 54
5

The Collections.reverse implies only to List which is just one type of Collection, you cannot cast a Queue to a List. But you can try casting it to a LinkedList as:

Collections.reverse((LinkedList)queue);

Details:

I doubt that there is a built-in API for reversing the queue. You could still follow a conventional way of doing that using a Stack as :

Stack<Integer> stack = new Stack<>();
while (!queue.isEmpty()) {
    stack.add(queue.remove());
}
while (!stack.isEmpty()) {
    queue.add(stack.pop());
}

and then convert to an array as you will

int[] res = queue.stream().mapToInt(Integer::intValue).toArray();

On the other hand, if a Deque satisfies your needs currently, you can simply rely on the LinkedList itself since it implements a Deque as well. Then your current implementation would be as simple as :

LinkedList<Integer> dequeue = new LinkedList<>();
Collections.reverse(dequeue);
int[] res = dequeue.stream().mapToInt(Integer::intValue).toArray();

whether the queue is reversed is not important. An int array of the reversed elements is what I need.

Another solution from what others have already suggested is to reverse the Stream of the queue and then mapToInt to convert to an array as :

Queue<Integer> queue = new LinkedList<>();
int[] res = reverse(queue.stream()).mapToInt(Integer::intValue).toArray();

This uses a utility reverse suggested by Stuart Marks in this answer such that:

@SuppressWarnings("unchecked")
static <T> Stream<T> reverse(Stream<T> input) {
    Object[] temp = input.toArray();
    return (Stream<T>) IntStream.range(0, temp.length)
            .mapToObj(i -> temp[temp.length - i - 1]);
}
Naman
  • 27,789
  • 26
  • 218
  • 353
  • You should probably not be using the `Stack` class since it extends `Vector` and is therefore synchronized, which is not needed here and only decreases performance. – Marcono1234 Jan 04 '19 at 02:39
  • 2
    If using a `Deque` it might be more efficient to use `Deque.descendingIterator()` combined with `Spliterators` and `StreamSupport`, assuming only the reversed array is needed and not the reversed `Deque`. The code will be more verbose, however. – Slaw Jan 04 '19 at 02:39
  • @Slaw It would be sure. Just that the intention of when I wrote the answer was to ensure the original store is reversed, but later the OP clarified that the reversed output is what matters eventually. – Naman Jan 04 '19 at 03:43
  • @Marcono1234 actualy, the JVM does away with the `synchronized` blocks within the `Vector` class is its most recent versions when it detects they're are not shared and their aquisition cost is negligible :) but yeah, on principle you shouldn't do that as `Vector` is not recommended to be used anymore. – João Rebelo Jan 04 '19 at 10:43
4

In Java8 version you can use Stream API to help you.

The skeleton of code like this:

int[] reversedQueue = queue.stream()
    .collect(Collector.of(() -> new ArrayDeque<Integer>(), ArrayDeque::addFirst, (a,b)->a))
    .stream().mapToInt(Integer::intValue).toArray();
TongChen
  • 1,414
  • 1
  • 11
  • 21
  • It looks like your combiner (`(a,b)->a`) is missing `b` in the result – Marcono1234 Jan 04 '19 at 02:52
  • @Marcono1234 There is no problem.The third parameter of `Collector.of` method is one `BinaryOperator` it's the combiner function for the new collector. In our code there only one collector,so can't miss any element in collector. – TongChen Jan 04 '19 at 03:24
  • it does not matter in this case probably since (if I understand it correctly) the combiner is only used for parallel streams. However, you should probably comment that the implementation is not correct, to prevent bugs in the future in case someone uses this code for a parallel stream. It should probably be `(a, b) -> {b.addAll(a); return b;}`. – Marcono1234 Jan 05 '19 at 00:00
3

Finally, I figure out this one line solution.

Integer[] intArray = queue.stream()
            .collect(LinkedList::new, LinkedList::addFirst, LinkedList::addAll)
            .toArray(new Integer[queue.size()]);

the int[] version should like

int[] intArray = queue.stream()
            .collect(LinkedList<Integer>::new, LinkedList::addFirst, LinkedList::addAll)
            .stream()
            .mapToInt(Integer::intValue)
            .toArray();
Keijack
  • 778
  • 6
  • 12
3

You can use the LazyIterate utility from Eclipse Collections as follows.

int[] res = LazyIterate.adapt(queue)
        .collectInt(i -> i)
        .toList()
        .asReversed()
        .toArray();

You can also use the Collectors2 class with a Java Stream.

int[] ints = queue.stream()
        .collect(Collectors2.collectInt(i -> i, IntLists.mutable::empty))
        .asReversed()
        .toArray();

You can stream the int values directly into a MutableIntList, reverse it, and then convert it to an int array.

int[] ints =
    IntLists.mutable.ofAll(queue.stream().mapToInt(i -> i)).asReversed().toArray();

Finally, you can stream the int values directly into a MutableIntStack and convert it to an int array.

int[] ints =
    IntStacks.mutable.ofAll(queue.stream().mapToInt(i -> i)).toArray();

Note: I am a committer for Eclipse Collections.

Donald Raab
  • 6,458
  • 2
  • 36
  • 44
2

This is one line, but it may not be very efficient:

int[] res = queue.stream()
                 .collect(LinkedList<Integer>::new, (l, e) -> l.addFirst(e), (l1, l2) -> l1.addAll(l2))
                 .stream()
                 .mapToInt(Integer::intValue)
                 .toArray();

If you want to be efficient and readable, you should continue using what you have now.

ZhaoGang
  • 4,491
  • 1
  • 27
  • 39
Jai
  • 8,165
  • 2
  • 21
  • 52
1

Here is a different solution using Stream and Collections.reverse() in one line of code:

Integer[] reversedArray = queue.stream()
        .collect(Collectors.collectingAndThen(Collectors.toList(),
                list -> {
                    Collections.reverse(list);
                    return list.toArray(new Integer[0]);
                }
        ));

OR

int[] reversedArray = queue.stream()
        .collect(Collectors.collectingAndThen(Collectors.toList(),
                list -> {
                    Collections.reverse(list);
                    return list.stream()
                            .mapToInt(Integer::intValue)
                            .toArray();
                }
        ));
aminography
  • 21,986
  • 13
  • 70
  • 74
1

Here's a way that creates a reversed array without reversing the queue:

int[] i = { queue.size() };
int[] array = new int[i[0]];
queue.forEach(n -> array[--i[0]] = n);

The above code is quite hacky due to the impossibility to modify local variables from within lambda expressions. So here i needs to be a one-element array, to overcome this restriction.

Note: bear in mind that I've come to this solution just for fun :)

fps
  • 33,623
  • 8
  • 55
  • 110