私は次のコードを持っています:
List<Object> list = new ArrayList<>();
list.addAll(method1());
if(list.isEmpty()) { list.addAll(method2()); }
if(list.isEmpty()) { list.addAll(method3()); }
if(list.isEmpty()) { list.addAll(method4()); }
if(list.isEmpty()) { list.addAll(method5()); }
if(list.isEmpty()) { list.addAll(method6()); }
return list;
ストリーム操作を使用して、条件付きで要素を追加する良い方法はありますか?リストが空の場合のみmethod2から要素を追加し、そうでない場合は戻ります。
編集:メソッドには重いロジックが含まれているため、実行を防ぐ必要があることに言及する価値があります。
サプライヤーのストリームを使用し、List.isEmpty
でフィルター処理するだけです。
Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.ifPresent(list::addAll);
return list;
findFirst()
は、メソッドの1つによって最初の空でないリストが返されたときに、methodN()
への不要な呼び出しを防ぎます。
EDIT:
以下のコメントで述べたように、list
オブジェクトが他の何かで初期化されていない場合、ストリームの結果を直接返すだけの意味があります。
return Stream.<Supplier<List<Object>>>of(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6())
.map(Supplier<List<Object>>::get)
.filter(l -> !l.isEmpty())
.findFirst()
.orElseGet(ArrayList::new);
addAll
の戻り値を確認してみてください。リストが変更されるたびにtrue
を返すので、これを試してください:
List<Object> list = new ArrayList<>();
// ret unused, otherwise it doesn't compile
boolean ret = list.addAll(method1())
|| list.addAll(method2())
|| list.addAll(method3())
|| list.addAll(method4())
|| list.addAll(method5())
|| list.addAll(method6());
return list;
遅延評価のため、少なくとも1つの要素を追加した最初のaddAll
操作は、残りが呼び出されないようにします。 「||」という事実が好きです意図を非常によく表現しています。
自分自身を繰り返さずにそれを行う方法は、それを行うメソッドを抽出することです:
private void addIfEmpty(List<Object> targetList, Supplier<Collection<?>> supplier) {
if (targetList.isEmpty()) {
targetList.addAll(supplier.get());
}
}
その後
List<Object> list = new ArrayList<>();
addIfEmpty(list, this::method1);
addIfEmpty(list, this::method2);
addIfEmpty(list, this::method3);
addIfEmpty(list, this::method4);
addIfEmpty(list, this::method5);
addIfEmpty(list, this::method6);
return list;
またはforループを使用することもできます。
List<Supplier<Collection<?>>> suppliers = Arrays.asList(this::method1, this::method2, ...);
List<Object> list = new ArrayList<>();
suppliers.forEach(supplier -> this.addIfEmpty(list, supplier));
DRYは最も重要な側面ではありません。元のコードの方が読みやすく、理解しやすいと思う場合は、そのままにしてください。
メソッドを作成することでコードをより良くすることができます
public void addAllIfEmpty(List<Object> list, Supplier<List<Object>> method){
if(list.isEmpty()){
list.addAll(method.get());
}
}
次に、このように使用できます(ClassName::method1
を使用して参照する必要がある場合、メソッドは静的メソッドではないと想定しました)
List<Object> list = new ArrayList<>();
list.addAll(method1());
addAllIfEmpty(list, this::method2);
addAllIfEmpty(list, this::method3);
addAllIfEmpty(list, this::method4);
addAllIfEmpty(list, this::method5);
addAllIfEmpty(list, this::method6);
return list;
本当にストリームを使用したい場合は、これを行うことができます
Stream.<Supplier<List<Object>>>of(this::method1, this::method2, this::method3, this::method4, this::method5, this::method6)
.collect(ArrayList::new, this::addAllIfEmpty, ArrayList::addAll);
IMOでは、メソッドがどのように参照されるかに応じて、より複雑になります。ループを使用した方がよい場合があります
次のようなメソッドを作成できます。
public static List<Object> lazyVersion(Supplier<List<Object>>... suppliers){
return Arrays.stream(suppliers)
.map(Supplier::get)
.filter(s -> !s.isEmpty()) // or .filter(Predicate.not(List::isEmpty)) as of JDK11
.findFirst()
.orElseGet(Collections::emptyList);
}
そして、次のように呼び出します:
lazyVersion(() -> method1(),
() -> method2(),
() -> method3(),
() -> method4(),
() -> method5(),
() -> method6());
説明のみを目的としたメソッド名。