Java - Streams

From My Limbic Wiki

Collections

  • Set (Ensemble): no repetitions, not sorted
  • List: can have repetitions, sorted

Main Methods

<source lang="Java"> - add(Object o) //Adds the specified object to the collection. - remove(Object o) //Removes the specified object from the collection. - clear() //Removes all elements from the collection. - size() //Returns an integer that indicates how many elements are currently in the collection. - iterator() //Returns an object that can be used to retrieve references to the elements in the collection. </source>

Samples

Print all Students name

<source lang="Java"> Student student; Iterator iterator = collection.iterator(); while (iterator.hasNext()) {

   student = (Student)(iterator.next());
   System.out.println(student.getFullName());

} </source>

Get a Student

<source lang="Java"> Student student = getNextStudent(); while (student != null) {

   collection.add(student);

} </source>

Iteration Without Generics Often Requires Casting

<source lang="Java"> Student student; Iterator iterator = collection.iterator(); while (iterator.hasNext()) {

   student = (Student)(iterator.next());
   System.out.println(student.getFullName());

} </source>

Autoboxing

is the process of performing the encapsulation before a primitive is stored in a collection, and the following is an example of how this can improve your code: <source lang="Java"> Random random = new Random(); Collection<Integer> collection = new ArrayList<Integer>(); for (int i = 0; i < 10; i++) {

   collection.add(random.nextInt());

} </source>

Unboxing

is the process of extracting the primitive value from its corresponding wrapper object when retrieving data from a collection: <source lang="Java"> int total = 0; Iterator<Integer> iterator = collection.iterator(); while (iterator.hasNext()) {

   total += iterator.next();

} </source>

Remove by Index

A Naive Approach to Removing Elements Corresponding to a Set of Index Values <source lang="java"> //as soon as you remove the first entry, the other indices in the array effectively become invalid because they no longer refer to the same elements for (int i = 0; i < deleteIndices.length; i++) {

  myList.remove(deleteIndices[i]);

}

//An easy way to address this problem is to simply traverse the index list in reverse order for (int i = deleteIndices.length - 1; i >= 0; i--) {

   myList.remove(deletedIndices[i]);

} </source>

Queue

Main Methods

<source lang="Java"> - element() Returns a reference to the head element without removing it, throwing an exception if the queue is empty. - peek() //Returns a reference to the head element without removing it, returning null if the queue is empty. - offer() //Adds an element to the queue. Some implementations may reject the addition, in which case a value of false is returned. - remove() //Retrieves a reference to the head element and removes it from the queue, throwing an exception if the queue is empty. - poll() //Retrieves a reference to the head element and removes it from the queue, returning null if the queue is empty. </source>

PriorityQueue

Like a TreeSet or a TreeMap in that its elements are ordered based upon their “priority,” which is really just either their natural order or their sequence as determined by an instance of Comparator

PriorityBlockingQueue

isn’t a subclass of PriorityQueue one important difference: as the name implies, this class represents a blocking queue. A blocking queue is one that causes the calling thread to be blocked if the queue is empty, and the thread will remain blocked until at least one element is added to the queue.

ArrayBlockingQueue

This class represents a blocking queue that uses an array to maintain its elements, and those elements are returned in FIFO manne You might use this class when threads are creating tasks that need to be processed and all the tasks are considered to be of equal priority.

LinkedBlockingQueue

This is a blocking queue that uses a linked list to maintain its elements, which are returned in FIFO order

ConcurrentLinkedQueue

ConcurrentLinkedQueue represents a queue that returns its elements in FIFO order but doesn’t block.

SynchronousQueue

This is a blocking queue that can’t contain any elements; instead, it blocks each request to add an element to the queue until it receives a request to retrieve an element from the queue, and vice versa.

DelayQueue

Only objects that implement the Delayed interface can be added to this queue This class is useful when you have a group of elements that are time sensitive.

Implementation

<source lang="Java"> public class DelayedReminder implements Delayed {

   private String reminderText;
   private long delayInSeconds;
   public DelayedReminder(String reminder, long seconds) {
       reminderText = reminder;
       delayInSeconds = seconds;
   }
   public String getReminderText() {
       return reminderText;
   }
   public long getDelay(TimeUnit timeUnit) {
       return TimeUnit.SECONDS.convert(delayInSeconds, timeUnit);
   }
   public int compareTo(Delayed delayed) {
       return (int)(delayInSeconds - delayed.getDelay(TimeUnit.SECONDS));
   }

}

DelayQueue queue = new DelayQueue(); DelayedReminder reminder = new DelayedReminder("Wake me up in 60 seconds", 60); queue.add(reminder); reminder = new DelayedReminder("Wake me up in 30 seconds", 30); queue.add(reminder); </source>

Streams

you’ll often need to execute some set of code using each object instance that’s stored in a collection Fortunately, one aspect of the Streams API is that it allows you to use multithreading to process a collection Streams API not only provides an easy way to process the objects in a collection but essentially gives you thread-safe processing for “free”—that is, with no work on your part.

The Streams API is used by defining a “stream,” which is Java code that consists of three parts

  • Data source: Where the data comes from, such as a List instance.
  • Intermediate operations: what should be done with the data, such as filtering or transforming it.
  • Terminal operation: what should be done with the processed data and when (or if) processing should be stopped

Classes to support functional-style operations on streams of elements, such as map-reduce transformations on collections. For example:

    int sum = widgets.stream()
                     .filter(b -> b.getColor() == RED)
                     .mapToInt(b -> b.getWeight())
                     .sum();

Here we use widgets, a Collection<Widget>, as a source for a stream, and then perform a filter-map-reduce on the stream to obtain the sum of the weights of the red widgets. (Summation is an example of a reduction operation.)

The key abstraction introduced in this package is stream. The classes Stream, IntStream, LongStream, and DoubleStream are streams over objects and the primitive int, long and double types. Streams differ from collections in several ways:

  • No storage. A stream is not a data structure that stores elements; instead, it conveys elements from a source such as a data structure, an array, a generator function, or an I/O channel, through a pipeline of computational operations.
  • Functional in nature. An operation on a stream produces a result, but does not modify its source. For example, filtering a Stream obtained from a collection produces a new Stream without the filtered elements, rather than removing elements from the source collection.
  • Laziness-seeking. Many stream operations, such as filtering, mapping, or duplicate removal, can be implemented lazily, exposing opportunities for optimization. For example, "find the first String with three consecutive vowels" need not examine all the input strings. Stream operations are divided into intermediate (Stream-producing) operations and terminal (value- or side-effect-producing) operations. Intermediate operations are always lazy.
  • Possibly unbounded. While collections have a finite size, streams need not. Short-circuiting operations such as limit(n) or findFirst() can allow computations on infinite streams to complete in finite time.
  • Consumable. The elements of a stream are only visited once during the life of a stream. Like an Iterator, a new stream must be generated to revisit the same elements of the source.

Streams can be obtained in a number of ways. Some examples include: From a Collection via the stream() and parallelStream() methods;

  • From an array via Arrays.stream(Object[]);
  • From static factory methods on the stream classes, such as Stream.of(Object[]), IntStream.range(int, int) or Stream.iterate(Object, UnaryOperator);
  • The lines of a file can be obtained from BufferedReader.lines();
  • Streams of file paths can be obtained from methods in Files;
  • Streams of random numbers can be obtained from Random.ints();
  • Numerous other stream-bearing methods in the JDK, including BitSet.stream(), Pattern.splitAsStream(java.lang.CharSequence), and JarFile.stream().

Additional stream sources can be provided by third-party libraries using these techniques.


Simple Stream Implementation

<source lang="java"> // Suppose that we have a collection of String instance // and we want to filter the collection and create a new collection containing only the objects from the original collection that begin with some prefix List<String> filteredList =

       myItems.stream()
       .filter(item -> item.startsWith(prefix))
       .collect(Collectors.toList());

</source>

Expanded Version of the Stream Example

<source lang="java"> // This anonymous inner class is equivalent to the lambda: item -> (item.startsWith(prefix)) Predicate<String> prefixSelector = new Predicate<String>() {

       public boolean test(String candidate)
       {
           return candidate.startsWith(prefix);
       }

}; Stream<String> dataSource = myItems.stream(); Stream<String> filtered = dataSource.filter(prefixSelector); Collector<String, ?, List<String>> collector = Collectors.toList(); List<String> myList = filtered.collect(collector); </source>


Stream Example That Contains Two Intermediate Operations

<source lang="java"> List<String> filteredList =

       myItems.stream()
       .filter(item -> item.startsWith(prefix))
       .map(item -> item.toLowerCase())
       .collect(Collectors.toList());

</source>

A voir

  • Parallel stream