[JAVA] [Memo] How to use Google MµG

A memo on how to use the Google MµG library, a utility library for Java 8.

Dependencies

dependencies {
    compile 'com.google.mug:mug:1.12'
}

BiStream

Stream with key and value.

Create a BiStream

Use the static method of BiStream.

biStream()

var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.biStream( input )
                     .mapValues( e -> e.toUpperCase() )
                     .toMap();
assertThat( result ).containsExactly( "foo", "FOO", "bar", "BAR", "buz", "BUZ" );

Wrap a regular Stream. When wondering what to use, it seems that it is to call an operation like mapKeys () afterwards.

var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.biStream( input, Function.identity(), e -> e.toUpperCase() )
                     .toMap();
assertThat( result ).containsExactly( "foo", "FOO", "bar", "BAR", "buz", "BUZ" );

A version that takes a mapping function as an argument.

from()

var input = Map.of( "foo", "123", "bar", "456", "buz", "789" );
var result = BiStream.from( input )
                     .map( ( i, v ) -> i + ":" + v )
                     .collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456", "buz:789" );

Create a BiStream with the map key and value as a pair.

indexed()

var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.indexed( input )
                     .map( ( i, v ) -> i + ":" + v )
                     .collect( toList() );
assertThat( result ).containsExactly( "0:foo", "1:bar", "2:buz" );

Add an index starting from 0. This is nice because it's built into the standard APIs of most programming languages. The return type is BiStream <Integer, V>. Primitive types are not supported as expected.

neighbors()

var input = Stream.of( "foo", "bar", "buz" );
var result = BiStream.neighbors( input )
                     .map( ( i, v ) -> i + ":" + v )
                     .collect( toList() );
assertThat( result ).containsExactly( "foo:bar", "bar:buz" );

Pair two adjacent values.

of()

var result = BiStream.of( "foo", "123", "bar", "456" )
                     .map( ( i, v ) -> i + ":" + v )
                     .collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456" );

Similar to the JDK standard ʻof ()`.

zip()

var input1 = Stream.of( "foo", "bar", "buz" );
var input2 = Stream.of( "hoge", "piyo", "fuga" );
var result = BiStream.zip( input1, input2 )
                     .map( ( t, u ) -> t + ":" + u )
                     .collect( toList() );
assertThat( result ).containsExactly( "foo:hoge", "bar:piyo", "buz:fuga" );

Combine the two Streams. I always made this myself. To be honest, the level I want you to add to the standard library. However, note that if the size of Stream is different, the remainder will be ignored according to the smaller one.

var input1 = Stream.of( "foo", "bar", "buz" );
var input2 = Stream.of( "hoge", "piyo" );
var result = BiStream.zip( input1, input2 )
                     .map( ( t, u ) -> t + ":" + u )
                     .collect( toList() );

assertThat( result ).containsExactly( "foo:hoge", "bar:piyo" );

Use BiStream

append()

var result = BiStream.of( "foo", "123" ).append( "bar", "456" )
                     .map( ( t, u ) -> t + ":" + u )
                     .collect( toList() );
assertThat( result ).containsExactly( "foo:123", "bar:456" );

Add a value after it.

inverse()

var result = BiStream.of( "foo", "123", "bar", "456" )
                     .inverse()
                     .map( ( t, u ) -> t + ":" + u )
                     .collect( toList() );
assertThat( result ).containsExactly( "123:foo", "456:bar" );

Exchange values for keys. I want you to add it to the standard Stream ...

MoreStream

Provides extensions not found in the JDK or Guava.

generate()

var result = MoreStreams.generate( 1, n -> n >= 9 ? Stream.empty() : Stream.of( n + 1 ) )
                        .collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5, 6, 7, 8, 9 );

A method for generating a * finite * stream. Stop generation when an empty stream is returned. Is there any use for it?

flatten()

var input = Stream.of(
        Stream.of( "foo", "bar", "buz" ),
        Stream.of( "hoge", "piyo", "fuga" )
);
var result = MoreStreams.flatten( input )
                        .collect( toList() );
assertThat( result ).containsExactly( "foo", "bar", "buz", "hoge", "piyo", "fuga" );

The same process should be possible with flatMap, but there is this method because it cannot handle infinite streams due to a Java bug. It seems that it was fixed in JDK 10.

dice()

var input = Stream.of( "foo", "bar", "buz", "hoge", "piyo" );
var result = MoreStreams.dice( input, 2 )
                        .collect( toList() );
assertThat( result.size() ).isEqualTo( 3 );
var list1 = result.get( 0 );
assertThat( list1 ).containsExactly( "foo", "bar" );
var list2 = result.get( 1 );
assertThat( list2 ).containsExactly( "buz", "hoge" );
var list3 = result.get( 2 );
assertThat( list3 ).containsExactly( "piyo" );

List Streams by specified size and put them together in a newStream <List> . For sequential streams, it is guaranteed that a list of the specified size will be returned except at the end of the stream. Not guaranteed for parallel streams.

By the way, I learned for the first time that such an operation is called dice. How is it different from window?

iterateOnce()

var input = Stream.of( "foo", "bar", "buz" );
for ( var e : MoreStreams.iterateOnce( input ) ) {
    assertThat( e ).isAnyOf( "foo", "bar", "buz" );
}

A method for iterating a stream with a for statement. Maybe I won't use it for the rest of my life.

iterateThrough()

expect(() -> {
    MoreStreams.iterateThrough( Stream.of( "foo" ), e -> {
        throw new Exception( "Checked Exception!" );
    });
}).throwsException( e -> {
    assertThat( e ).hasMessageThat().isEqualTo( "Checked Exception!" );
});

It's basically the same as forEach (), except that you can throw a checked exception.

Retryer

A process for retrying a process that may fail. I think it's convenient, but most of the processes that require retries are provided with a retry function in the first place, so it's surprisingly useless.

  1. Create an instance of Retryer.
  2. Specify the retry conditions with ʻupon (), ʻifReturns (), etc.
  3. Specify the execution interval with Delay. In most cases, you can use ʻexponentialBackoff ()` to double the retry interval.
  4. Specify the process to retry with retryBlockingly (), retry (), retryAsync ().

Sync

var result = new Retryer()
        .upon( RuntimeException.class, Retryer.Delay.ofMillis( 100 ).exponentialBackoff( 2, 2 ) )
        .retryBlockingly( this::mayFail );
assertThat( result ).isEqualTo( "success" );

asynchronous

CompletionStage<String> future = new Retryer()
        .upon( RuntimeException.class, Retryer.Delay.ofMillis( 100 ).exponentialBackoff( 2, 2 ) )
        .retry( this::mayFail, Executors.newSingleThreadScheduledExecutor() );
future.thenAccept( result -> {
    assertThat( result ).isEqualTo( "success" );
});

To be honest, the asynchronous API is subtle.

Maybe

A class for wrapping processing that may raise an exception. Something like a version of java.util.Optional with the ability to handle exceptions. Is Try relatively close in Scala?

The main purpose seems to be to handle checked exceptions in streams easily.

maybe()

The basic method for creating a Maybe instance. Accepts various functional interfaces.

var result = IntStream.range( 1, 10 ).boxed()
                      .map( Maybe.maybe( e -> {
                          if ( e <= 5 ) {
                              return e;
                          } else {
                              throw new Exception();
                          }
                      } ) )
                      .map( m -> m.orElse( e -> Integer.valueOf( 0 ) ) )
                      .collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5, 0, 0, 0, 0 );

The value that caused the exception in ʻorElse ()is converted to0`.

expect(() -> {
    Maybe.maybe( () -> { throw new RuntimeException( "Unchecked!" );} );
}).throwsException( e -> {
    assertThat( e ).hasMessageThat().isEqualTo( "Unchecked!" );
});

Unchecked exceptions are rethrown as is. Caution. It's not that it can be used for anything other than streams, but due to this limitation, I think it's better to use a dedicated library.

byValue()

var result = IntStream.range( 1, 10 ).boxed()
                      .map( Maybe.maybe( e -> {
                          if ( e <= 5 ) {
                              return e;
                          } else {
                              throw new Exception();
                          }
                      } ) )
                      .filter( Maybe.byValue( n -> n % 2 == 0 ) )
                      .map( m -> m.orElse( e -> Integer.valueOf( 0 ) ) )
                      .collect( toList() );
assertThat( result ).containsExactly( 2, 4, 0, 0, 0, 0 );

Since filter () will receive Maybe, there is a method to unwrap it. As you can see, if an exception occurs, it moves on to the subsequent processing.

1.PNG 2.PNG

catching()

var result = IntStream.range( 1, 10 ).boxed()
                      .map( Maybe.maybe( e -> {
                          if ( e <= 5 ) {
                              return e;
                          } else {
                              throw new Exception();
                          }
                      } ) )
                      .flatMap( m -> m.catching( e -> {} ) )
                      .collect( toList() );
assertThat( result ).containsExactly( 1, 2, 3, 4, 5 );

Use when you just want to ignore the value.

Funnel

It's quite a niche to use, but it seems convenient if you get hooked.

Use Case

Funnel

var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> {
    assertThat( data ).containsExactly( "batch1", "batch2" );
    return data.stream().map( String::toUpperCase ).collect( toList() );
} );

var input = List.of( "local1", "batch1", "local2", "batch2" );
for ( var i : input ) {
    if ( i.startsWith( "local" ) ) {
        funnel.add( i );
    } else {
        batch.accept( i );
    }
}
var result = funnel.run();

assertThat( result ).containsExactly( "local1", "BATCH1", "local2", "BATCH2" );
  1. First, instantiate Funnel
  2. Specify the batch conversion process with the through () method.
  3. Call Funnel.add () for values to be processed individually.
  4. Pass the values to be processed together to Funnel.Batch.accept ().
  5. Run with Funnel.run ().

Note that ʻaccept ()` has an overload that takes a function that converts the result as an argument.

var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> List.of() );
batch.accept( "batch1" );
expect(() -> {
    funnel.run();
}).throwsException( e -> {
    assertThat( e ).isInstanceOf( IllegalStateException.class );
});

Obviously, if the size of the batch return list is different from the input, you will get an error.

var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> List.of() );
batch.accept( "batch1" );

expect(() -> {
    funnel.run();
}).throwsException( e -> {
    assertThat( e ).isInstanceOf( IllegalStateException.class );
});

The order depends on the order of batch processing. It is necessary to ensure that the process returns in the same order as the input order.

var funnel = new Funnel<String>();
var batch = funnel.<String>through( data -> {
    var newList = new ArrayList<String>( data );
    Collections.reverse( newList );
    return newList;
} );
batch.accept( "batch1" );
batch.accept( "batch2" );
var result = funnel.run();

assertThat( result ).containsExactly( "batch2", "batch1" );

Parallelizer

As the name suggests, parallelize processing.

var input = Stream.of( "foo", "bar", "buz" );
var result = Collections.synchronizedList( new ArrayList<String>() );
new Parallelizer( executor, 3 )
        .parallelize( input, result::add );
assertThat( result ).containsExactly( "foo", "bar", "buz" );
  1. Instantiate Parallelizer. The arguments are ʻExecutorService to execute the process and the number of tasks that can be executed at the same time. Is it basically the same as the number of threads in ʻExecutor Service?
  2. Call parallelize () or parallelizeUninterruptibly (). The first argument is Stream or ʻIterator`, which is the input data, and the second argument is the processing to be performed.

The process is blocked until it finishes. If an exception occurs in the middle, subsequent processing is interrupted.

According to the README

Parallel streams are for CPU-bound tasks. JDK has built-in magic to optimally use the available cores. Parallelizer is for IO-bound tasks.

It seems that it should be used for file input / output, external API call, etc.

Summary

Recommended Posts

[Memo] How to use Google MµG
How to use Google Colaboratory
How to use cron (personal memo)
[Memo] How to use BeautifulSoup4 (1) Display html
How to use Google Test in C
How to use Google Assistant on Windows 10
How to use xml.etree.ElementTree
How to use Python-shell
How to use tf.data
How to use virtualenv
How to use Seaboan
How to use image-match
How to use shogun
How to use Pandas 2
How to use numpy.vectorize
How to use pytest_report_header
How to use partial
How to use Bio.Phylo
How to use SymPy
How to use x-means
How to use WikiExtractor.py
How to use IPython
How to use Matplotlib
How to use iptables
How to use numpy
How to use TokyoTechFes2015
How to use venv
How to use dictionary {}
How to use Pyenv
How to use list []
How to use python-kabusapi
How to use OptParse
How to use return
How to use dotenv
How to use pyenv-virtualenv
How to use Go.mod
How to use imutils
How to use import
How to use Qt Designer
How to use search sorted
[gensim] How to use Doc2Vec
python3: How to use bottle (2)
How to use the generator
[Python] How to use list 1
How to use Python argparse
How to use IPython Notebook
How to use Pandas Rolling
[Note] How to use virtualenv
How to use redis-py Dictionaries
Python: How to use pydub
[Python] How to use checkio
[Go] How to use "... (3 periods)"
How to use Django's GeoIp2
[Python] How to use input ()
How to use the decorator
[Introduction] How to use open3d
How to use Python lambda
How to use Jupyter Notebook
[Python] How to use virtualenv
python3: How to use bottle (3)
python3: How to use bottle