web-dev-qa-db-ja.com

このループをリファクタリングするにはどうすればよいですか?

Itemと呼ばれるクラスにプリミティブ配列とリストを使用するアプリケーションがあります。これらは、レガシーの理由で交換可能に使用されます(これが1つのタイプにすぎないことを願っていますが、それはそうです)。

次に、for-eachループを介して機能するこのような新しいメソッドを追加する必要があります。

public void something(Item... items) {
    for (Item i : items) {
        doStuff();
    }
}

public void something(List<Item> items) {
    for (Item i : items) {
        doStuff();
    }
}

言い換えれば、プリミティブ配列とリストの両方でまったく同じメソッドを2回使用します。これを単一のメソッドにうまくリファクタリングする方法はありますか?

46
Selbi

君は できない 単一のメソッドでこれを行うべきではありません(*)。 _Item[]_と_List<Item>_は無関係な型です。

オーバーロードの1つをもう1つを呼び出す必要があります。something(Item... items)something(List<Item>)を呼び出すか、something(List<Item>)something(Item... items)を呼び出します。

2つのオプションのうち、配列のオーバーロードはリストのオーバーロードを呼び出す方が適切です。

_public void something(Item... items) {
  something(Arrays.asList(item));
}
_

配列をコピーするのではなく、むしろラップするため、これは安価です。Listの作成はO(1)です。

リストオーバーロードから配列オーバーロードを呼び出す場合:

_public void something(List<Item> items) {
  something(items.toArray(new Item[0]));
}
_

toArray呼び出しは配列を作成してデータを設定する必要があるため、これはより高価になります。これはO(n)操作であり、nはリストのサイズです。ただし、somethingListの内容を置き換えることができないというわずかな利点があります。これは、配列への更新は実行後に単に破棄されるためです。


(*)あなたはcanですが、他の一般的なスーパータイプはないので、Objectパラメーターを受け入れなければならないので、本当にグロスで、タイプセーフではありません_List<Item>_および_Item[]_;それでも、2つのタイプのループを繰り返す必要があります。そして、(実行時に)完全に無関係な型が渡される可能性を処理する必要があります。

_public void something(Object obj) {
  if (obj instanceof List) {
    for (Object element : (List<?>) obj) {
      Item item = (Item) element;  // Potential ClassCastException.
      doStuff();
    }
  } else if (obj instanceof Item[]) {
    for (Item item : (Item[]) obj) {
      doStuff();
    }
  } else {
    throw new IllegalArgumentException();
  }
}
_

なんて混乱だ。過負荷をメーカーに感謝します。

68
Andy Turner

Java 8を使用する場合、単に forEach または map を呼び出すこともできます] Stream 以上で完了です。例えば.

_yourStream.forEach(doStuff());
_

ここで、doStuff()consumer 文字列を処理するか、文字列を処理したくない場合はyourStream.forEach(s -> doStuff())を使用し、_do stuff_だけにします。

次のようにしてストリームを取得できます。

_Stream.of(yourArray) // or Arrays.stream(yourArray)
      .forEach(doStuff());
_

そしてあなたのリストのために:

_list.stream()
    .forEach(doStuff());
_

ストリームを使用する主な利点は、おそらく読みやすさです。パフォーマンスに関しては失われる可能性があり、ストリームを取得するためだけに_Stream.of/Arrays.stream_またはCollection.stream()を呼び出したくない場合にも失われる可能性があります。

本当にsomething(...)メソッドを保持したい場合(可変引数とリストの両方に対応できる場合)、オーバーロードされたメソッドが必要です。または Andy Turnerの提案 を使用しますObject- parameter-method。

17
Roland

1つのメソッドを実装できます。この場合、2番目のメソッドはパラメーターとしてリストを持っているためです。最初のメソッドの代わりに、Arrays.asList(items)を使用してリスト内の配列を変換してから、最初のメソッドを呼び出すことができます。そのため、最終的には、1つのメソッド(パラメーターとしてリストを持つ)のみが作成されます。

また、アイテムリストに要素がほとんどない場合は、Java 8:のラムダ式を使用できます。

items.foreach(item -> doStuff(item));

そのため、1つのループのみを含むメソッドは作成されず、コードが読みやすくなります。

1
eve2017

Listを配列に変換してから渡すことでこれを実現する必要があります。

これを単一の方法として保持し、

_public void something(Item... items) {
   for (Item i : items) {
       doStuff();
   }
}
_

__List<Item>_を渡したい場合、次のように渡します。

something(listItem.toArray(new Item[listItem.size()]))

0
jack jay