web-dev-qa-db-ja.com

Java 8リスト処理-要素を条件付きで追加

私は次のコードを持っています:

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から要素を追加し、そうでない場合は戻ります。

編集:メソッドには重いロジックが含まれているため、実行を防ぐ必要があることに言及する価値があります。

51
ionut

サプライヤーのストリームを使用し、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);
45
ernest_k

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操作は、残りが呼び出されないようにします。 「||」という事実が好きです意図を非常によく表現しています。

67
Dorian Gray

自分自身を繰り返さずにそれを行う方法は、それを行うメソッドを抽出することです:

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は最も重要な側面ではありません。元のコードの方が読みやすく、理解しやすいと思う場合は、そのままにしてください。

17
JB Nizet

メソッドを作成することでコードをより良くすることができます

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では、メソッドがどのように参照されるかに応じて、より複雑になります。ループを使用した方がよい場合があります

11
Ricola

次のようなメソッドを作成できます。

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());

説明のみを目的としたメソッド名。

6
Aomine