JDK1.8の新機能
1 はじめに
JDK1.8は長い間リリースされており、すでに多くの企業で使用されています。また、Spring5 および SpringBoot2.0 では JDK1.8 以降を使用することを推奨します。したがって、私たちは時代に遅れずに変化を受け入れなければなりません。
このバージョンの Jdk8 には、言語、コンパイラ、ライブラリ、ツール、および JVM に 10 を超える新機能が含まれています。この記事では、次の新機能について学びます。
- ラムダ式
- 機能インターフェイス
- メソッドリファレンス]
- インターフェースのデフォルトメソッドと静的メソッド
- オプション
- ストリーム
- 並列アレイ
2. ラムダ式
関数型プログラミング
クロージャとしても知られるラムダ式は、Java 8 のリリースを推進する最も重要な新機能です。Lambda では、関数をメソッドのパラメータとして渡すことができます (関数はパラメータとしてメソッドに渡されます)。コードをより簡潔かつコンパクトにすることができます。
2.1 基本的な構文:
(参数列表) -> {代码块}
注意が必要です:
- パラメータの型は省略可能で、コンパイラが独自に推論できます。
- パラメータが 1 つだけの場合は括弧を省略できます
- コード ブロックが 1 行のコードのみの場合は、中括弧も省略できます。
- コードブロックが 1 行で、結果を含む式の場合は
return
省略できます。
注:実際、ラムダ式は匿名内部クラスの短縮形であると考えてください。もちろん、前提として、匿名内部クラスはインターフェイスに対応する必要があり、インターフェイスには関数が 1 つだけ存在する必要があります。ラムダ式は、パラメータリスト、コード本体、戻り値、その他の情報を関数に直接記述することです用函数来代替完整的匿名内部类
。
2.2 使用例
例 1: 複数のパラメータ
コレクションを準備します。
// 准备一个集合
List<Integer> list = Arrays.asList(10, 5, 25, -15, 20);
コレクションをソートしたいとします。まず JDK7 の書き込みメソッドを見てみましょう。匿名の内部クラスを通じてメソッドを構築する必要がありますComparator
。
// Jdk1.7写法
Collections.sort(list,new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
System.out.println(list);// [-15, 5, 10, 20, 25]
jdk8 の場合、新しいコレクション API:sort(Comparator c)
メソッドを使用してコンパレータを受け取り、Lambda を使用してComparator
匿名内部クラスを置き換えます。
// Jdk1.8写法,参数列表的数据类型可省略:
list.sort((i1,i2) -> {
return i1 - i2;});
System.out.println(list);// [-15, 5, 10, 20, 25]
のメソッドを比較するComparator
と、ここで記述された Lambda 式はメソッドの短縮形にcompare()
すぎず、JDK8 はそれを匿名の内部クラスにコンパイルすることがわかります。compare()
もっと簡単ではないでしょうか!
心配しないでください、ここのコード ブロックには 1 行のコードしかなく、前述の省略ルールに従っていることがわかりました。
// Jdk8写法
// 因为代码块是一个有返回值的表达式,可以省略大括号以及return
list.sort((i1,i2) -> i1 - i2);
例 2: 単一パラメータ
先ほどのコレクションを例に挙げると、コレクション内の要素を走査して印刷したいと考えます。
まず jdk1.7 メソッドを使用します。
// JDK1.7遍历并打印集合
for (Integer i : list) {
System.out.println(i);
}
foreach()
jdk1.8 は、要素を操作する関数を受け取るメソッドをコレクションに追加します。
// JDK1.8遍历并打印集合,因为只有一个参数,所以我们可以省略小括号:
list.forEach(i -> System.out.println(i));
例 3: Lambda を変数に代入する
ラムダ式の本質は実際には匿名の内部クラスであるため、実際にラムダ式を変数に割り当てることができます。
// 将一个Lambda表达式赋值给某个接口:
Runnable task = () -> {
// 这里其实是Runnable接口的匿名内部类,我们在编写run方法。
System.out.println("hello lambda!");
};
new Thread(task).start();
ただし、上記のような使い方は稀で、Lambdaを直接パラメータとして使用するのが一般的です。
例 4: 暗黙的な最終的な例
Lambda 式の本質は実際には匿名内部クラスであり、匿名内部クラスが外部ローカル変数にアクセスするときは、変数を ! として宣言する必要がありますfinal
。ただし、Lambda 式を使用する場合は宣言する必要はありませんfinal
。これは、Lambda の最下層が暗黙的に変数を に設定し、final
後続の操作で変数を変更してはいけないため、匿名内部クラスのルールに違反することを意味するものではありません。 :
正しい例:
// 定义一个局部变量
int num = -1;
Runnable r = () -> {
// 在Lambda表达式中使用局部变量num,num会被隐式声明为final
System.out.println(num);
};
new Thread(r).start();// -1
エラーの場合:
// 定义一个局部变量
int num = -1;
Runnable r = () -> {
// 在Lambda表达式中使用局部变量num,num会被隐式声明为final,不能进行任何修改操作
System.out.println(num++);
};
new Thread(r).start();//报错
3. 機能インターフェイス
前回の学習を経て、皆さんはラムダ式について予備的な理解ができたと思います。結論は:
- ラムダ式は、インターフェイスの匿名内部クラスの短縮形です。
- インターフェイスは次の条件を満たす必要があります: 内部には関数が 1 つだけあります
Runnable
実際、そのようなインターフェイスは関数型インターフェイスと呼ばれ、私たちが学んだものはComparator
関数型インターフェイスの典型的な代表例です。しかし実際には、関数型インターフェイスは非常に脆弱なので、誰かがインターフェイスにメソッドをもう 1 つ追加する限り、そのインターフェイスは関数型インターフェイスではなくなり、コンパイルは失敗します。@FunctionalInterface
Java 8では、上記の脆弱性を克服し、機能インターフェイスを明示的に示すための特別なアノテーションが提供されています。@FunctionalInterface
さらに、jdk8 バージョンでは、次のような多くの既存のインターフェイスに注釈が追加されましたRunnable
。
[外部リンク画像の転送に失敗しました。ソース サイトにはリーチ防止メカニズムがある可能性があります。画像を保存して直接アップロードすることをお勧めします (img-ODT4QdNs-1669729296892)(runnable.png)]
さらに、Jdk8 は、デフォルトで使用できるいくつかの機能インターフェイスを提供します。
3.1 関数型インターフェース
@FunctionalInterface
public interface Function<T, R> {
// 接收一个参数T,返回一个结果R
R apply(T t);
}
Function はパラメータを持ち、値を返す関数を表します。同様の Function インターフェイスが多数あります。
インターフェース名 | 説明 |
---|---|
BiFunction<T,U,R> |
T 型と U 型の 2 つの引数を受け取り、R 型の結果を返す関数 |
DoubleFunction<R> |
double 型パラメータを受け取り、R 型の結果を返す関数 |
IntFunction<R> |
int型パラメータを受け取り、R型の結果を返す関数 |
LongFunction<R> |
long 型パラメータを受け取り、R 型の結果を返す関数 |
ToDoubleFunction<T> |
T 型パラメータを受け取り、double 型の結果を返す |
ToIntFunction<T> |
T型パラメータを受け取り、int型の結果を返す |
ToLongFunction<T> |
T型パラメータを受け取り、long型の結果を返す |
DoubleToIntFunction |
double型のパラメータを受け取り、int型の結果を返す |
DoubleToLongFunction |
double型パラメータを受け取り、long型の結果を返す |
パターンが見えますか?これらは Function から派生した関数インターフェイスのクラスで、パラメーターが戻り結果を決定しないことを指定するか、結果がパラメーターの型を認識しないことを指定するか、あるいはその両方を指定します。
3.2 コンシューマシリーズ
@FunctionalInterface
public interface Consumer<T> {
// 接收T类型参数,不返回结果
void accept(T t);
}
Function シリーズと同様に、Consumer シリーズにもさまざまな派生インターフェイスがありますが、ここには記載されていません。ただし、これらはすべて同様の特性を持っています。つまり、結果を返さないということです。
3.3 述語シリーズ
@FunctionalInterface
public interface Predicate<T> {
// 接收T类型参数,返回boolean类型结果
boolean test(T t);
}
Predicateシリーズのパラメータは固定ではありませんが、戻り値はboolean型でなければなりません。
3.4 サプライヤーシリーズ
@FunctionalInterface
public interface Supplier<T> {
// 无需参数,返回一个T类型结果
T get();
}
サプライヤーシリーズ、英語訳は「供給者」、その名が示すように、出力のみで回収はありません。したがって、パラメータは受け入れず、タイプ T の結果を返します。
4. メソッドのリファレンス
メソッド参照を使用すると、開発者は既存のメソッドを変数として渡すことができます。メソッド参照はラムダ式で使用できます。
4.1 構文:
メソッド参照には 4 つのタイプがあります。
文法 | 説明 |
---|---|
クラス名::静的メソッド名 | クラスの静的メソッドへの参照 |
クラス名::非静的メソッド名 | クラスの非静的メソッドへの参照 |
インスタンス オブジェクト::非静的メソッド名 | クラスの指定されたインスタンス オブジェクトへの非静的メソッド参照 |
クラス名::新規 | クラスコンストラクターリファレンス |
4.2 例
まず、収集ツール クラスを作成し、メソッドを提供します。
public class CollectionUtil{
/**
* 利用function将list集合中的每一个元素转换后形成新的集合返回
* @param list 要转换的源集合
* @param function 转换元素的方式
* @param <T> 源集合的元素类型
* @param <R> 转换后的元素类型
* @return
*/
public static <T,R> List<R> convert(List<T> list, Function<T,R> function){
List<R> result = new ArrayList<>();
list.forEach(t -> result.add(function.apply(t)));
return result;
}
}
このメソッドが 2 つのパラメーターを受け取ることがわかります。
List<T> list
: 変換する必要があるコレクションFunction<T,R>
: 関数インターフェイス。型 T を受け取り、型 R を返します。この関数インターフェイスを使用して、リスト内の要素 T を R 型に変換します。
次に、具体的なケースを見てみましょう。
4.2.1 クラスの静的メソッド参照
List<Integer> list = Arrays.asList(1000, 2000, 3000);
Integer.toHexString()
このコレクション内の要素を 16 進数に変換し、メソッドを呼び出す必要があります。
public static String toHexString(int i) {
return toUnsignedString0(i, 4);
}
このメソッドは i 型を受け入れ、関数インターフェイスString
の構築に使用できる型を返しますFunction
。
まず、Lambda の元の記述方法に従いましょう。受信した Lambda 式はインターフェイスにコンパイルされ、元のコレクションの要素がFunction
インターフェイス内で変換されます。Integer.toHexString(i)
// 通过Lambda表达式实现
List<String> hexList = CollectionUtil.convert(list, i -> Integer.toHexString(i));
System.out.println(hexList);// [3e8, 7d0, bb8]
Integer.toHexString()
上記のラムダ式コード ブロックには、メソッドへの参照のみがあり、他のコードは存在しないため、メソッドをパラメータとして直接渡すことができ、コンパイラがそれを処理します。これは静的メソッド参照です。:
// 类的静态方法引用
List<String> hexList = CollectionUtil.convert(list, Integer::toHexString;
System.out.println(hexList);// [3e8, 7d0, bb8]
4.2.2 クラスの非静的メソッド参照
次に、String クラスの toUpperCase() メソッドを使用して、生成したString
コレクション内の要素を大文字にします。hexList
public String toUpperCase() {
return toUpperCase(Locale.getDefault());
}
今回はクラス名では呼び出すことができない非静的メソッドですが、インスタンスオブジェクトを使用するため、以前の実装といくつかの違いがありますが、コレクション内の各文字列を受け取りますs
。ただし、上記とは異なり、パラメーターではなく呼び出し元がs
異なります。toUpperCase()
// 通过Lambda表达式,接收String数据,调用toUpperCase()
List<String> upperList = CollectionUtil.convert(hexList, s -> s.toUpperCase());
System.out.println(upperList);// [3E8, 7D0, BB8]
コード本体にはtoUpperCase()
正しい呼び出しのみがあるため、メソッドをパラメーター参照として渡すことができ、さらに次のように省略することもできます。
// 类的成员方法
List<String> upperList = CollectionUtil.convert(hexList, String::toUpperCase);
System.out.println(upperList);// [3E8, 7D0, BB8]
4.2.3 インスタンスを指定する非静的メソッド参照
次の要件は次のとおりです。最初に数値を定義しInteger num = 2000
、次にこの数値を使用してコレクション内の各数値と比較し、比較結果を新しいコレクションに入れます。Integer
オブジェクトを比較するには、次のメソッドを使用できますcompareTo
。
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
まずLambdaを使って実装します。
List<Integer> list = Arrays.asList(1000, 2000, 3000);
// 某个对象的成员方法
Integer num = 2000;
List<Integer> compareList = CollectionUtil.convert(list, i -> num.compareTo(i));
System.out.println(compareList);// [1, 0, -1]
前のものと同様に、ここでも Lambda コード ブロックにはnum.compareTo(i)
正しい呼び出しのみがあるため、省略できます。ただし、メソッドの呼び出し元はコレクションの要素ではなく、外部ローカル変数であるため、この方法ではメソッドの呼び出し元num
を特定できないため、使用できないことに注意してください。Integer::compareTo
呼び出し元を指定するには、次を使用する必要があります对象::方法名
。
// 某个对象的成员方法
Integer num = 2000;
List<Integer> compareList = CollectionUtil.convert(list, num::compareTo);
System.out.println(compareList);// [1, 0, -1]
4.2.4 コンストラクターの参照
最後のシーン: コレクション内の数値をミリ秒値として使用し、Date
オブジェクトを構築してコレクションに入れます。ここでは Date のコンストラクターを使用する必要があります。
/**
* @param date the milliseconds since January 1, 1970, 00:00:00 GMT.
* @see java.lang.System#currentTimeMillis()
*/
public Date(long date) {
fastTime = date;
}
コレクション内の各要素を受け取り、その要素をDate
コンストラクター パラメーターとして渡すことができます。
// 将数值类型集合,转为Date类型
List<Date> dateList = CollectionUtil.convert(list, i -> new Date(i));
// 这里遍历元素后需要打印,因此直接把println作为方法引用传递了
dateList.forEach(System.out::println);
上記のラムダ式の実装では、コード本体のnew Date()
コードは 1 行のみであるため、メソッド参照によって省略することもできます。しかし、問題は、コンストラクターには名前がなく、new
代わりにキーワードのみを使用できることです。
// 构造方法
List<Date> dateList = CollectionUtil.convert(list, Date::new);
dateList.forEach(System.out::println);
次の 2 つの点に注意してください。
- 上記のコードの System.out::println は、実際には、指定されたオブジェクト System.out の非静的メソッド println への参照です。
- 複数のコンストラクターがある場合、区別できず転送が失敗する可能性があります。
5. インターフェースのデフォルトメソッドと静的メソッド
Java 8 では、デフォルト メソッドと静的メソッドという 2 つの新しい概念によってインターフェイスの意味が拡張されています。
5.1 デフォルトの方法
デフォルト メソッドを使用すると、開発者はバイナリ互換性を損なうことなく、既存のインターフェイスに新しいメソッドを追加できます。つまり、インターフェイスを実装するクラスに、新しく追加されたメソッドの実装も強制しません。
デフォルト メソッドと抽象メソッドの違いは、抽象メソッドには実装が必要ですが、デフォルト メソッドには実装が必要ないことです。インターフェイスによって提供されるデフォルトのメソッドは、インターフェイスの実装クラスによって継承またはオーバーライドされます。コード例は次のとおりです。
private interface Defaulable {
// Interfaces now allow default methods, the implementer may or
// may not implement (override) them.
default String notRequired() {
return "Default implementation";
}
}
private static class DefaultableImpl implements Defaulable {
}
private static class OverridableImpl implements Defaulable {
@Override
public String notRequired() {
return "Overridden implementation";
}
}
Defaultable インターフェースは、default キーワードを使用してデフォルトのメソッド notRequired() を定義します。DefaultableImpl クラスはこのインターフェイスを実装し、このインターフェイスのデフォルト メソッドをデフォルトで継承します。OverridableImpl クラスもこのインターフェイスを実装しますが、インターフェイスのデフォルト メソッドをオーバーライドして、別の実装を提供します。
5.2 静的メソッド
Java 8 によってもたらされたもう 1 つの興味深い機能は、静的メソッドをインターフェースで定義でき、インターフェースを使用してこれらの静的メソッドを直接呼び出すことができることです。コード例は次のとおりです。
private interface DefaulableFactory {
// Interfaces now allow static methods
static Defaulable create( Supplier< Defaulable > supplier ) {
return supplier.get();
}
}
次のコード スニペットは、デフォルト メソッドと静的メソッドの使用シナリオを組み合わせたものです。
public static void main( String[] args ) {
// 调用接口的静态方法,并且传递DefaultableImpl的构造函数引用来构建对象
Defaulable defaulable = DefaulableFactory.create( DefaultableImpl::new );
System.out.println( defaulable.notRequired() );
// 调用接口的静态方法,并且传递OverridableImpl的构造函数引用来构建对象
defaulable = DefaulableFactory.create( OverridableImpl::new );
System.out.println( defaulable.notRequired() );
}
このコードの出力は次のとおりです。
Default implementation
Overridden implementation
JVM 上のデフォルト メソッドの実装はバイトコード レベルでのサポートを提供するため、非常に効率的です。デフォルトのメソッドを使用すると、既存の継承階層を壊すことなくインターフェイスを改善できます。公式ライブラリでのこの機能の応用は、、 などの新しいjava.util.Collection
メソッドをインターフェイスに追加することです。stream()
parallelStream()
forEach()
removeIf()
デフォルトのメソッドには非常に多くの利点がありますが、実際の開発では注意して使用する必要があります。複雑な継承システムでは、デフォルトのメソッドはあいまいさやコンパイル エラーを引き起こす可能性があります。さらに詳しく知りたい場合は、公式ドキュメントを参照してください。
6. オプション
Java アプリケーションで最も一般的なバグは null 例外です。
Optional
T 型または 型の値を保存できるコンテナーですnull
。明示的なチェックを回避するための便利なインターフェイスがいくつか提供されていますnull
。詳細については、公式 Java 8 ドキュメントを参照してください。
Optional の使用例を見てみましょう。空の値または特定の型の値です。
Optional< String > fullName = Optional.ofNullable( null );
System.out.println( "Full Name is set? " + fullName.isPresent() );
System.out.println( "Full Name: " + fullName.orElseGet( () -> "[none]" ) );
System.out.println( fullName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
Optional
インスタンスが null 以外の値を保持している場合、isPresent()
メソッドは を返しtrue
、それ以外の場合は を返します。インスタンスが を保持しているfalse
場合、メソッドはラムダ式によって生成されたデフォルト値を受け入れることができ、メソッドは既存のインスタンスの値を新しい値に変換できます。 ;メソッドは同じですメソッドは似ていますが、Lambda によって生成されるのではなく、null が保持されている場合に渡されるデフォルト値を返します。Optional
null
orElseGet()
map()
Optional
orElse()
orElseGet()
上記のコードの出力は次のようになります。
Full Name is set? false
Full Name: [none]
Hey Stranger!
別の簡単な例を見てみましょう。
Optional< String > firstName = Optional.of( "Tom" );
System.out.println( "First Name is set? " + firstName.isPresent() );
System.out.println( "First Name: " + firstName.orElseGet( () -> "[none]" ) );
System.out.println( firstName.map( s -> "Hey " + s + "!" ).orElse( "Hey Stranger!" ) );
System.out.println();
この例の出力は次のとおりです。
First Name is set? true
First Name: Tom
Hey Tom!
詳細については、公式ドキュメントを参照してください。
7. ストリーム
新しい Stream API (java.util.stream) は、実稼働環境の関数型プログラミングを Java ライブラリにもたらします。これは Java ライブラリに対するこれまでで最大の改善であり、開発者はより効率的で簡潔かつコンパクトなコードを作成できるようになります。
Steam API はコレクション操作を大幅に簡素化します (コレクションだけではなく、後で説明します)。まず、Task と呼ばれるこのクラスを見てください。
public class Streams {
private enum Status {
OPEN, CLOSED
};
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status, final Integer points ) {
this.status = status;
this.points = points;
}
public Integer getPoints() {
return points;
}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format( "[%s, %d]", status, points );
}
}
}
Task クラスには Points プロパティがあり、OPEN または CLOSED の 2 つの状態があります。ここで、タスクのコレクションがあると仮定します。
final Collection< Task > tasks = Arrays.asList(
new Task( Status.OPEN, 5 ),
new Task( Status.OPEN, 13 ),
new Task( Status.CLOSED, 8 )
);
まず質問を見てください: このタスク セットには OPEN 状態がいくつありますか? ポイント属性の合計を計算します。Java 8 より前では、この問題を解決するには、foreach ループを使用してタスク コレクションを走査する必要がありましたが、Java 8 では、steam を使用して問題を解決できます。これには、一連の要素のリストが含まれており、順次処理と並列処理がサポートされています。 。
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks
.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println( "Total points: " + totalPointsOfOpenTasks );
このメソッドを実行すると、コンソール出力は次のようになります。
Total points: 18
ここには言及する価値のある知識ポイントがたくさんあります。最初に、tasks
コレクションが表現に変換されます。2番目にsteam
、それらをすべて除外するための操作が行われます。3 番目に、コレクション内の各インスタンスのメソッドに基づいて、操作によってストリームがコレクションに変換されます。最後に、メソッドによって合計が計算され、結果として最終結果。steam
filter
CLOSED
task
mapToInt
tasks
task
Task::getPoints
task
Integer
sum
次の例を学習する前に、steam に関するいくつかの知識ポイントを覚えておく必要があります (詳細については、ここをクリックしてください)。Steam での操作は、中期操作と後期操作に分けられます。
中間操作は新しい蒸気を返します。中間操作 (フィルターなど) を実行すると、実際の濾過操作は実行されませんが、新しい蒸気が作成され、元の蒸気に含まれる適格な要素が新しく作成された蒸気に入れられます。
後の操作 (forEach や sum など) はスチームを横断して結果または付随的な結果を取得します。後の操作の実行後、スチーム処理ラインは処理されているため使用できません。ほとんどの場合、遅い操作はすぐに Steam を通過します。
steam のもう 1 つの価値は、並列処理 (並列処理) の創造的なサポートです。上記の一連のタスクについては、次のコードを使用して、すべてのタスクのポイントの合計を計算できます。
// Calculate total points of all tasks
final double totalPoints = tasks
.stream()
.parallel()
.map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce( 0, Integer::sum );
System.out.println( "Total points (all tasks): " + totalPoints );
ここでは、Parallel メソッドを使用してすべてのタスクを並列処理し、reduce メソッドを使用して最終結果を計算します。コンソール出力は次のとおりです。
Total points(all tasks): 26.0
コレクションの場合、多くの場合、特定の条件に従ってコレクション内の要素をグループ化する必要があります。steam が提供する API を使用すると、このようなタスクをすばやく完了できます。コードは次のとおりです。
// Group tasks by their status
final Map< Status, List< Task > > map = tasks
.stream()
.collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
コンソール出力は次のとおりです。
{
CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
タスク セットに関する最後の質問例は、セット内の各タスクのポイントの割合を計算する方法です。具体的な処理コードは次のとおりです。
// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks
.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> ( long )( weigth * 100 ) ) // LongStream
.mapToObj( percentage -> percentage + "%" ) // Stream< String>
.collect( Collectors.toList() ); // List< String >
System.out.println( result );
コンソール出力は次のとおりです。
[19%, 50%, 30%]
最後に、前に述べたように、Steam API は Java コレクションに作用するだけでなく、従来の IO 操作 (ファイルまたはネットワークから 1 行ずつデータを読み取る) は Steam 処理の恩恵を受けることができます。ここに小さな例を示します。
final Path path = new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {
lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );
}
ストリームのメソッドは、ストリームのメソッドが呼び出されたときに実行されるonClose()
追加のハンドルを含む同等のストリームを返します。close()
インターフェイスのデフォルト メソッドと静的メソッドによってサポートされるストリーム API、ラムダ式、メソッド参照は、ソフトウェア開発の現代のパラダイムに対する Java 8 の応答です。
8. 並列配列
Java 8 バージョンには、配列の並列処理をサポートするための新しいメソッドが多数追加されています。最も重要なことはparallelSort()
、マルチコア マシンでの配列のソートを大幅に高速化できることです。次の例は、ParallelXxx ファミリのメソッドを示しています。
package com.javacodegeeks.java8.parallel.arrays;
import java.util.Arrays;
import java.util.concurrent.ThreadLocalRandom;
public class ParallelArrays {
public static void main( String[] args ) {
long[] arrayOfLong = new long [ 20000 ];
Arrays.parallelSetAll( arrayOfLong,
index -> ThreadLocalRandom.current().nextInt( 1000000 ) );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
Arrays.parallelSort( arrayOfLong );
Arrays.stream( arrayOfLong ).limit( 10 ).forEach(
i -> System.out.print( i + " " ) );
System.out.println();
}
}
上記のコードは、ParallelSetAll() メソッドを使用して 20,000 の乱数を生成し、次いで、ParallelSort() メソッドを使用してそれらを並べ替えます。このプログラムは、順序なし配列と並べ替え配列の両方の最初の 10 要素を出力します。上記のコード例の出力は次のとおりです。
Unsorted: 591217 891976 443951 424479 766825 351964 242997 642839 119108 552378
Sorted: 39 220 263 268 325 607 655 678 723 793