Surprisingly deep Java list inversion-Stream processing

Note that it was unexpectedly difficult to invert the list in Java.

[1, 2, 3].reverse

In ruby, the array is easily inverted as shown in ↑. I thought that Java would also have inversion as standard, such as Stream API,

list.stream().reverse();

I couldn't do this. I searched for various streams, but I couldn't find it, so I was in trouble.

For the time being, the folly of turning the list from the opposite side with a for statement to generate an inverted list. .. ..

So, I tried to summarize ** 3 ways to invert the list **.

1. How to use the most common Collections.reverse

This is the most standard method provided by the standard library.

List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f");
Collections.reverse(list);
// list = [f, e, d, c, b, a]

Note that this process changes list destructively. By the way, Collections is a collection of processes for manipulating collections such as List and Set. For example, shuffle and sort.

2. How to use Deque

If you frequently traverse the structure to the right and left, it's a good idea to turn it into a bidirectional list.

Deque<String> deque = new LinkedList<>(list);
//order
for(String s: deque) {
	//Processing of s
}
//Reverse
Iterator<String> iter = deque.descendingIterator();
while(iter.hasNext()) {
	String s = iter.next();
	//Processing of s
}

However, the drawback is that it becomes a little legacy writing. .. ..

3. How to use Collect of Stream API

You can also use the Stream API. Stream processing itself is basically independent of the order of elements, and is good at processing independently for each element. On the other hand, we are not good at processing that involves order relations. However, the order can be guaranteed and can be used to invert.

//A little long. .. .. However, if the process of obtaining the reverse order list does not appear many times
List<String> reversedList = list.stream().collect(ArrayList::new, (l, e) -> l.add(0, e), (l, subl) -> l.addAll(0, subl));

collect is a method that describes the aggregation process.

  1. ʻArrayList :: new` determines the class for aggregation.
  2. (l, e)-> l.add (0, e) is the body of the aggregation process. Now add an element to the beginning of list l.
  3. (l, subl)-> l.addAll (0, subl) is a summary process during parallel execution. Summarize in the first argument. (At the time of parallel processing, the list is appropriately divided and processed, and the aggregated result is output for each. It is necessary to integrate these multiple results.)

This is a simple thing to do, but it is troublesome to write. ** I really want to write as follows. ** **

List<String> reversedList = strs.stream().collect(MyCollectors.reverse());

In this case, prepare the following Collection implementation class.

MyCollectors.java


public class MyCollectors {

	public static <T> Collector<T, List<T>, List<T>> reverse() {

		return new Collector<T, List<T>, List<T>>() {
			@Override
			public Supplier<List<T>> supplier() {
				return ArrayList::new;
			}

			@Override
			public BiConsumer<List<T>, T> accumulator() {
				return (l, e) -> l.add(0, e);
			}

			@Override
			public BinaryOperator<List<T>> combiner() {
				return (l, subl) -> {
					l.addAll(0, subl);
					return l;
				};
			}

			@Override
			public Function<List<T>, List<T>> finisher() {
				//No final aggregation process is required, so the list is returned as is
				return l -> l;
			}

			@Override
			public Set<Characteristics> characteristics() {
				//Parallel processing possible
				return EnumSet.of(Characteristics.CONCURRENT);
			}
		};
	}
}

It's a hassle to write, but once you write it, you can easily use it with stream processing.

Bonus) In some cases it is not necessary to generate an inverted list

If the inverted list itself is an intermediate product, there is no need to invert the list. For example, if you want to combine strings in reverse order, you don't need to create an inverted list, just do the following:

List<String> strs = Arrays.asList("a", "b", "c", "d", "e", "f");
String reverse = strs.stream().reduce((e1, e2) -> e2.concat(e1)).get();
// reverse = "fedcba"

Summary

In the case of Java, the chain method cannot be easily done with just one word, but there are three main methods. 0. Use Java standard Collections.reverse

  1. Use Deque
  2. Use Stream API It seems that you need to use the method that suits the individual process.

Recommended Posts

Surprisingly deep Java list inversion-Stream processing
Java memorandum (list)
Clone Java List.
Java thread processing
Java string processing
[Java] Multi-thread processing
[Java] Stream processing
About List [Java]
java iterative processing
[Note] Java: Speed of List processing by purpose
[java] sort in list
List processing to understand with pictures --java8 stream / javaslang-
JAVA constructor call processing
Java random, various processing
[Memo] Java Linked List
List processing to understand with pictures --java8 stream / javaslang --bonus
[Java] List type / Array type conversion
[Java] Multi-thread processing --Exclusive control
[Java] Stream API --Stream termination processing
[Java] Stream API --Stream intermediate processing
[Java] List OS-dependent standard libraries
Measured parallel processing in Java
Understanding Java Concurrent Processing (Introduction)
List aggregation in Java (Collectors.groupingBy)
Java List Group, Sort, etc.
Deep copy collection in Java
List data structure [Java / Scala]
Summary of java error processing
[Java] Shallow copy and deep copy when converting an array to List