次のJava update
、つまりJava 8 or JDK 8
を確認しています。はい、私はせっかちです、多くの新しいものがありますが、私が理解していない何か、いくつかの簡単なコードがあります:
final Stream<Integer>stream = Stream.of(1,2,3,4,5,6,7,8,9,10);
stream.flatMap();
javadocsは
public <R> Stream<R> flatMap(Function<? super T,? extends Stream<? extends R>> mapper)
このストリームの各要素を、指定されたマッピング関数を各要素に適用することによって生成されたマッピングされたストリームの内容で置き換えた結果で構成されるストリームを返します。マップされた各ストリームは、そのコンテンツがこのストリームに配置された後に閉じられます。 (マップされたストリームがnullの場合、代わりに空のストリームが使用されます。)これは中間操作です。
誰かがflatMap
に関する簡単な実際の例を作成してくれたら、以前のJavaバージョンJava[6,7]
でそれをどのようにコーディングでき、Java 8
を使用して同じルーチンをコーディングできるかを評価します。
flatMap
には意味がありません Stream 質問で示したStream<Integer>
のように、すでにフラットです。
ただし、Stream<List<Integer>>
があれば、それは理にかなっており、これを行うことができます:
Stream<List<Integer>> integerListStream = Stream.of(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
);
Stream<Integer> integerStream = integerListStream .flatMap(Collection::stream);
integerStream.forEach(System.out::println);
どちらが印刷されます:
1
2
3
4
5
これをJava 8より前に行うには、ループが必要です。
List<List<Integer>> integerLists = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5)
)
List<Integer> flattened = new ArrayList<>();
for (List<Integer> integerList : integerLists)
{
flattened.addAll(integerList);
}
for (Integer i : flattened)
{
System.out.println(i);
}
1、2、2、3、3、3、4、4、4、4などのシーケンス(つまり、1x1、2x2、3x3など)を作成するとします。
flatMap
を使用すると、次のようになります。
IntStream sequence = IntStream.rangeClosed(1, 4)
.flatMap(i -> IntStream.iterate(i, identity()).limit(i));
sequence.forEach(System.out::println);
ここで:
IntStream.rangeClosed(1, 4)
は、1から4までのint
のストリームを作成しますIntStream.iterate(i, identity()).limit(i)
は、int
iの長さiのストリームを作成します。したがって、i = 4
に適用すると、ストリームが作成されます:4, 4, 4, 4
flatMap
は、ストリームを「フラット化」し、元のストリームに「連結」しますJava <8では、2つのネストされたループが必要になります。
List<Integer> list = new ArrayList<>();
for (int i = 1; i <= 4; i++) {
for (int j = 0; j < i; j++) {
list.add(i);
}
}
各TimeSeries
が本質的にList<TimeSeries>
であるMap<LocalDate, Double>
があるとします。時系列の少なくとも1つに値があるすべての日付のリストを取得したい。 flatMap
の助け:
list.stream().parallel()
.flatMap(ts -> ts.dates().stream()) // for each TS, stream dates and flatmap
.distinct() // remove duplicates
.sorted() // sort ascending
.collect(toList());
読みやすいだけでなく、突然10万個の要素を処理する必要がある場合は、parallel()
を追加するだけで、並行コードを作成しなくてもパフォーマンスが向上します。
フレーズのリストからASCでソートされた一意の単語を抽出します。
List<String> phrases = Arrays.asList(
"sporadic perjury",
"confounded skimming",
"incumbent jailer",
"confounded jailer");
List<String> uniqueWords = phrases
.stream()
.flatMap(phrase -> Stream.of(phrase.split(" +")))
.distinct()
.sorted()
.collect(Collectors.toList());
System.out.println("Unique words: " + uniqueWords);
...および出力:
Unique words: [confounded, incumbent, jailer, perjury, skimming, sporadic]
巻き戻しリストが退屈だと思うのは私だけですか? ;-)
オブジェクトを試してみましょう。ちなみに実世界の例。
指定:反復タスクを表すオブジェクト。重要なタスクフィールドについて:リマインダーはstart
で鳴り始め、repeatPeriod
repeatUnit
(例:5時間)ごとに繰り返され、合計でrepeatCount
リマインダーがあります(開始を含む)。
目標:タスクアラームの呼び出しごとに1つずつ、タスクコピーのリストを作成します。
List<Task> tasks =
Arrays.asList(
new Task(
false,//completed sign
"My important task",//task name (text)
LocalDateTime.now().plus(2, ChronoUnit.DAYS),//first reminder(start)
true,//is task repetitive?
1,//reminder interval
ChronoUnit.DAYS,//interval unit
5//total number of reminders
)
);
tasks.stream().flatMap(
x -> LongStream.iterate(
x.getStart().toEpochSecond(ZoneOffset.UTC),
p -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds())
).limit(x.getRepeatCount()).boxed()
.map( y -> new Task(x,LocalDateTime.ofEpochSecond(y,0,ZoneOffset.UTC)))
).forEach(System.out::println);
出力:
Task{completed=false, text='My important task', start=2014-10-01T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-02T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-03T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-04T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
Task{completed=false, text='My important task', start=2014-10-05T21:35:24, repeat=false, repeatCount=0, repeatPeriod=0, repeatUnit=null}
追伸:誰かがもっと簡単な解決策を提案してくれれば、私は結局のところプロではありません。
PDATE: @RBzは詳細な説明を求めたので、ここにあります。基本的にflatMapは、別のストリーム内のストリームのすべての要素を出力ストリームに配置します。ここで多くのストリーム:)。したがって、初期ストリームのラムダ式の各タスクに対して、x -> LongStream.iterate...
は、タスクの開始時刻を表す長い値のストリームを作成します。このストリームはx.getRepeatCount()
インスタンスに制限されています。値はx.getStart().toEpochSecond(ZoneOffset.UTC)
から始まり、次の各値はラムダp -> (p + x.getRepeatPeriod()*x.getRepeatUnit().getDuration().getSeconds()
を使用して計算されます。 boxed()
は、各long値を含むストリームをLongラッパーインスタンスとして返します。次に、そのストリーム内の各Longは、繰り返しのない新しいタスクインスタンスにマップされ、正確な実行時間が含まれます。このサンプルには、入力リストに1つのタスクのみが含まれています。しかし、あなたが千を持っていると想像してください。これで、タスクオブジェクトの1000ストリームのストリームができます。ここでflatMap
が行うことは、すべてのストリームからのすべてのタスクを同じ出力ストリームに配置することです。それはすべて私が理解しているとおりです。ご質問ありがとうございます!
非常に簡単な例:姓または名に関係なく、フルネームのリストを分割して名前のリストを取得します
List<String> fullNames = Arrays.asList("Barry Allen", "Bruce Wayne", "Clark Kent");
fullNames.stream()
.flatMap(fullName -> Pattern.compile(" ").splitAsStream(fullName))
.forEach(System.out::println);
これは印刷します:
Barry
Allen
Bruce
Wayne
Clark
Kent
このメソッドは1つのFunctionを引数として受け取り、この関数は1つのパラメーターTを入力引数として受け入れ、パラメーターRの1つのストリームを戻り値として返します。この関数がこのストリームの各要素に適用されると、新しい値のストリームが生成されます。次に、各要素によって生成されたこれらの新しいストリームのすべての要素が新しいストリームにコピーされ、このメソッドの戻り値になります。
これを考えると:
public class SalesTerritory
{
private String territoryName;
private Set<String> geographicExtents;
public SalesTerritory( String territoryName, Set<String> zipCodes )
{
this.territoryName = territoryName;
this.geographicExtents = zipCodes;
}
public String getTerritoryName()
{
return territoryName;
}
public void setTerritoryName( String territoryName )
{
this.territoryName = territoryName;
}
public Set<String> getGeographicExtents()
{
return geographicExtents != null ? Collections.unmodifiableSet( geographicExtents ) : Collections.emptySet();
}
public void setGeographicExtents( Set<String> geographicExtents )
{
this.geographicExtents = new HashSet<>( geographicExtents );
}
@Override
public int hashCode()
{
int hash = 7;
hash = 53 * hash + Objects.hashCode( this.territoryName );
return hash;
}
@Override
public boolean equals( Object obj )
{
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
final SalesTerritory other = (SalesTerritory) obj;
if ( !Objects.equals( this.territoryName, other.territoryName ) ) {
return false;
}
return true;
}
@Override
public String toString()
{
return "SalesTerritory{" + "territoryName=" + territoryName + ", geographicExtents=" + geographicExtents + '}';
}
}
この:
public class SalesTerritories
{
private static final Set<SalesTerritory> territories
= new HashSet<>(
Arrays.asList(
new SalesTerritory[]{
new SalesTerritory( "North-East, USA",
new HashSet<>( Arrays.asList( new String[]{ "Maine", "New Hampshire", "Vermont",
"Rhode Island", "Massachusetts", "Connecticut",
"New York", "New Jersey", "Delaware", "Maryland",
"Eastern Pennsylvania", "District of Columbia" } ) ) ),
new SalesTerritory( "Appalachia, USA",
new HashSet<>( Arrays.asList( new String[]{ "West-Virgina", "Kentucky",
"Western Pennsylvania" } ) ) ),
new SalesTerritory( "South-East, USA",
new HashSet<>( Arrays.asList( new String[]{ "Virginia", "North Carolina", "South Carolina",
"Georgia", "Florida", "Alabama", "Tennessee",
"Mississippi", "Arkansas", "Louisiana" } ) ) ),
new SalesTerritory( "Mid-West, USA",
new HashSet<>( Arrays.asList( new String[]{ "Ohio", "Michigan", "Wisconsin", "Minnesota",
"Iowa", "Missouri", "Illinois", "Indiana" } ) ) ),
new SalesTerritory( "Great Plains, USA",
new HashSet<>( Arrays.asList( new String[]{ "Oklahoma", "Kansas", "Nebraska",
"South Dakota", "North Dakota",
"Eastern Montana",
"Wyoming", "Colorada" } ) ) ),
new SalesTerritory( "Rocky Mountain, USA",
new HashSet<>( Arrays.asList( new String[]{ "Western Montana", "Idaho", "Utah", "Nevada" } ) ) ),
new SalesTerritory( "South-West, USA",
new HashSet<>( Arrays.asList( new String[]{ "Arizona", "New Mexico", "Texas" } ) ) ),
new SalesTerritory( "Pacific North-West, USA",
new HashSet<>( Arrays.asList( new String[]{ "Washington", "Oregon", "Alaska" } ) ) ),
new SalesTerritory( "Pacific South-West, USA",
new HashSet<>( Arrays.asList( new String[]{ "California", "Hawaii" } ) ) )
}
)
);
public static Set<SalesTerritory> getAllTerritories()
{
return Collections.unmodifiableSet( territories );
}
private SalesTerritories()
{
}
}
これを行うことができます:
System.out.println();
System.out
.println( "We can use 'flatMap' in combination with the 'AbstractMap.SimpleEntry' class to flatten a hierarchical data-structure to a set of Key/Value pairs..." );
SalesTerritories.getAllTerritories()
.stream()
.flatMap( t -> t.getGeographicExtents()
.stream()
.map( ge -> new SimpleEntry<>( t.getTerritoryName(), ge ) )
)
.map( e -> String.format( "%-30s : %s",
e.getKey(),
e.getValue() ) )
.forEach( System.out::println );