6

リストの要素のプロパティの 1 つを使用Listして a を aに変換するこのメソッドがあります。Map

要するに、次のようになります。

private Map<String, List<Diagnostic<? extends JavaFileObject>>> toMap( List<Diagnostic<? extends JavaFileObject>> diagnostics ) {
    Map<String, List<Diagnostic<? extends JavaFileObject>>> result = new HashMap<String, List<Diagnostic<? extends JavaFileObject>>>();
    for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
        List<Diagnostic<? extends JavaFileObject>> list = null;
        if ( !result.containsKey( d.getCode() ) ) {
            list = new ArrayList<Diagnostic<? extends JavaFileObject>>();
            result.put( d.getCode(), list );
        } else {
            list = result.get( d.getCode() );
        }
        assert list != null;
        list.add( d );
    }
    return result;
}

イヤッ!..

私はジェネリックが大好きで、以前は Java を使用していましたが、キャストエブリシングの時代には戻りたくありませんが、ジェネリックにジェネリック要素が要素として含まれていると、問題が発生します。

Java1.7 では " diamond " 演算子を使用できることはわかっていますが、別の方法があるはずです。

これは、非汎用バージョンでは次のようになります。

private Map toMap( List diagnostics ) { 
    Map result = new HashMap();
    for( Object o  : diagnostics ) {
        Diagnostic d = ( Diagnostic ) o; 
        List list = null;
        if( !result.containsKey( d.getCode() ) ) { 
            list = new ArrayList();
            result.put( d.getCode() , list );
         } else { 
            list = result.get( d.getCode() );
         }
         assert list != null;
         list.add( d );
     }
     return result;
}

おおよそ、私はそれをコンパイルしようとしませんでした。

他の言語はこれをどのように処理しますか? たとえば C#?、Scala? 私は SML や Haskell が処理する方法がとても気に入りましたが、あまりにも多くの魔法が害を及ぼす可能性があると思います (ただし、これはもちろん主観的です)

これに対する回避策はありますか?

4

7 に答える 7

7

You define one type parameter named T. Then you can use T within your generic like this:

private <T extends JavaFileObject> Map<String, List<Diagnostic<T>> toMap(List<Diagnostic<T> diagnostics) {
    Map<String, List<Diagnostic<T>> result = new HashMap<String, List<Diagnostic<T>>();
    for (Diagnostic<T> d : diagnostics ) {
        List<Diagnostic<T>> list = null;
        if ( !result.containsKey(d.getCode())) {
            list = new ArrayList<Diagnostic<T>>();
            result.put( d.getCode(), list );
        } else {
            list = result.get( d.getCode() );
        }
        assert list != null;
        list.add( d );
    }
    return result;
}

Above you will see the type parameter defined as <T extends JavaFileObject> and you reuse T everywhere you need to. This will make it a bit cleaner.

于 2011-07-05T22:05:04.483 に答える
4

Scala では、これは次のようになります。

// collections are immutable by default, but we want the mutable flavour
import collection.mutable

// An alias so we don't keep repeating ourself
type DiagMultiMap[T] = mutable.Map[String, mutable.Set[Diagnostic[T]]]

//pimp DiagMultiMap with the addDiagnostic method
class MapDiag[T](theMap: DiagMultiMap[T]) {
  def addDiagnostic(d: Diagnostic[T]): Unit = {
    val set = theMap.getOrElseUpdate(d.getCode) {mutable.Set.empty}
    set += d
  }
}

//an implicit conversion to enable the pimp
implicit def mapDiagPimp[T](theMap: DiagMultiMap[T]) = new MapDiag(theMap)

//This is how we make one
def mkDiagnosticMultiMap[T](entries: Seq[Diagnostic[T]]): DiagMultiMap[T] = {
  val theMap = new mutable.HashMap[String, mutable.Set[Diagnostic[T]]]()
  entries foreach { theMap addDiagnostic _ }
  theMap
}

コードにアクセスできないため、テストされていませんDiagnostic


アップデート

これは夜遅くに投稿することを教えてくれます。実際にははるかに簡単です...

Diagnosticオブジェクトの任意のシーケンスが与えられた場合:

val diags = List(new Diagnostic(...), new Diagnositic(...), ...)

1 つのメソッドで簡単にグループ化できます。

val diagMap = diags.groupBy(_.getCode)

しかし、それはそれよりも少し複雑です!

より大きな問題はDiagnostic、これが Java 標準ライブラリの一部であるため、バリアンス アノテーションを使用して書き直すことができないことです (詳細はコードの後で説明します)。ただし、ラッパーはそのトリックを実行しますが、幸いなことに、それほど大きくはありません。

class RichDiagnostic[S+](underlying: Diagnostic[S]) {
  def code: String = underlying.getCode
  def columnNumber: Long = underlying.getColumnNumber
  def endPosition: Long = underlying.getEndPosition
  def kind: Diagnostic.Kind = underlying.getKind
  def lineNumber: Long = underlying.getLineNumber
  def messageFor(locale: Locale): String = underlying.getMessage(locale) 
  def position: Long = underlying.getPosition
  def source: S = underlying.getSource
  def startPosition: Long = underlying.getStartPosition
  implicit def toUnderlying: Diagnostic[S] = underlying
}

+inはこの[S+]クラスを共変としてマークするため、 aが のサブクラスである場合、 aは のRichDiagnostic[A]サブクラスと見なされます。これは厄介な一般的な署名を回避するための鍵です。RichDiagnostic[B]AB<? extends T><? super T>

使い方も簡単です:

val richDiags = diags.map(d => new RichDiagnostic(d))
val diagMap = richDiags.groupBy(_.code)

Diagnostics がもともと Java List として提供されている場合、次のようなメソッドmapは自動的には使用できませんが、変換は簡単です。

import collection.JavaConverters._

//the toList isn't strictly necessary, but we get a mutable Buffer otherwise
val richDiags = diagsFromJava.asScala.toList.map(d => new RichDiagnostic(d))
val diagMap = richDiags.groupBy(_.code)

このコレクションの構築は 1 回限りの操作であり、基になるリストにエントリが追加された場合は繰り返す必要がありますが、問題にはならないと思います。

于 2011-07-07T15:28:35.673 に答える
3

素晴らしい例です。ジェネリック バージョンには、19 個の型引数があります。未加工バージョンでは、キャストは 1 つだけです。これは単なるプライベート メソッドであるため、生のバージョンを使用します。より公開されている場合でも、生のメソッド本体を保持できますが、完全なジェネリック シグネチャがあります。おそらく次のようなもの

Map<String, List<Diagnostic<? extends JavaFileObject>>> 
toMap( List<Diagnostic<? extends JavaFileObject>> diagnostics )
{
    Map result = new HashMap();
    for( Diagnostic d  : diagnostics ) 
    {
        List list = (List)result.get( d.getCode() );
        if(list==null)
            result.put( d.getCode(), list=new ArrayList());
         list.add( d );
    }
    return result;
}

署名のより一般的な入力と Java 7 を使用すると、次のことができます。

<D extends Diagnostic<?>>
Map<String, List<D>> toMap( List<D> diagnostics )
{
    Map<String, List<D>> result = new HashMap<>();
    for( D d  : diagnostics ) 
    {
        List<D> list = result.get( d.getCode() );
        if(list==null)
            result.put( d.getCode(), list=new ArrayList<>());
         list.add( d );
    }
    return result;
}

void test()
{
    List<Diagnostic<? extends JavaFileObject>> x = null;

    Map<String, List<Diagnostic<? extends JavaFileObject>>> map = toMap(x);
}

8 つの型引数。

于 2011-07-05T22:07:15.557 に答える
2

個人的には、このようなものを壊してみます(Eclipseがコンパイルされました-実行しようとしませんでした)

private class MapDiag extends HashMap<String, List<Diagnostic<? extends JavaFileObject>>>{
    private static final long serialVersionUID = 1L;

    void add(Diagnostic<? extends JavaFileObject> d){
      List<Diagnostic<? extends JavaFileObject>> list = null;
      if (containsKey(d.getCode())){
        list = get(d.getCode());
      }
      else {
        list = new ArrayList<Diagnostic<? extends JavaFileObject>>();
        put( d.getCode(), list );
      }
      list.add(d);
    }
  }

  private MapDiag toMap2( List<Diagnostic<? extends JavaFileObject>> diagnostics ) {
    MapDiag result = new MapDiag();
    for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
      result.add(d);
    }
    return result;
  }
于 2011-07-06T21:43:05.487 に答える
1

ここでのコメントのいくつかによって「答え」に到達したと思いますが、これまでのところ誰も標準的な定式化を行っていないと思います。

private <T extends Diagnostic<? extends JavaFileObject>>
        Map<String, List<T>> toMap(List<T> diagnostics) {
    Map<String, List<T>> result = new HashMap<String, List<T>>();
    for (T d : diagnostics) {
        List<T> list = null;
        if (!result.containsKey(d.getCode())) {
            list = new ArrayList<T>();
            result.put(d.getCode(), list);
        } else {
            list = result.get(d.getCode());
        }
        assert list != null;
        list.add(d);
    }
    return result;
}

typeパラメータの導入により、シグネチャの表現力を維持しながら、メソッドの内部が大幅に簡素化されます。

これは、提起された質問とは異なる方法ですが、バランスをとると、おそらくより正確であることに注意してください。ここで示したメソッドの違いにより、診断のパラメーター化されたタイプがメソッドの入力と出力の両方で同じになることが保証されます。

残念ながら、この場合、2つのコンストラクターを呼び出すと、型パラメーター(特にマップ)をさらに使用できなくなりますが、キャストを許可する場合は、メソッドをさらに簡潔にすることができます。

于 2011-07-07T11:08:30.763 に答える
1

まず、あなたの方法は間違っていませんか?...つまり、もっと似ているべきではありませんか?

List<T> list = null;
if (!result.containsKey(d.getCode())) {
    list = newArrayList();          
} else {
    list = result.get(d.getCode());
}   
result.put(d.getCode(), list);

また、ある種の型推論を提供する静的ユーティリティ メソッドを使用して、常にダイヤモンド オペレーターをエミュレートできます。つまり

public static <K, V> HashMap<K, V> newHashMap() {
    return new HashMap<K, V>();
}

public static <T> ArrayList<T> newArrayList() {
    return new ArrayList<T>();
}

そして、あなたの方法は次のようになります

private Map<String, List<Diagnostic<? extends JavaFileObject>>> toMap(List<Diagnostic<? extends JavaFileObject>> diagnostics) {
    Map<String, List<Diagnostic<? extends JavaFileObject>>> result = newHashMap();
    for (Diagnostic<? extends JavaFileObject> d : diagnostics) {
        List<Diagnostic<? extends JavaFileObject>> list = null;
        if (!result.containsKey(d.getCode())) {
            list = newArrayList();
            result.put(d.getCode(), list);
        } else {
            list = result.get(d.getCode());
        }
        assert list != null;
        list.add(d);
    }
    return result;
}

少なくともインスタンス化は小さくなります.... google guava ライブラリを使用している場合は、このユーティリティ メソッドが既にある可能性があることに注意してください。これをカーテン・ドッグの答えと組み合わせると、

    private <T extends Diagnostic<? extends JavaFileObject>> Map<String, List<T>> toMap(List<T> diagnostics) {
    Map<String, List<T>> result = newHashMap();
    for (T d : diagnostics) {
        List<T> list = null;
        if (!result.containsKey(d.getCode())) {
            list = newArrayList();
            result.put(d.getCode(), list);
        } else {
            list = result.get(d.getCode());
        }
        assert list != null;
        list.add(d);
    }
    return result;
}
于 2011-07-07T15:35:01.090 に答える
0

ここにいるみんなの提案をマッシュアップして、これが私がやったことです:

DiagnosticListラップする新しいクラスを作成しましたArrayList<Diagnostic<? extends JavaFileObject>>

それは非常に簡単です:

static final class DiagnosticList 
extends ArrayList<Diagnostic<? extends JavaFileObject>>{
    // no arg constructor 
    public DiagnosticList(){}
    // Using a list
    public DiagnosticList(List<Diagnostic<? extends JavaFileObject>> diagnostics){
        super( diagnostics);
    }
}

そして、メソッドの署名を変更できました。

private Map<String, DiagnosticList> toMap( DiagnosticList diagnostics ) {
    Map<String, DiagnosticList> result = new HashMap<String, DiagnosticList>();
    for ( Diagnostic<? extends JavaFileObject> d : diagnostics ) {
        DiagnosticList list = result.get(d.getCode());
        if( list == null ) {
          result.put( d.getCode(), (list = new DiagnosticList()));
        }
        list.add( d );
    }
    return result;
}

これはかなり読みやすいです。

元のプログラムのセマンティクスを変更する可能性はありますが、保守性にはメリットがあると思います。

于 2011-07-07T16:30:49.397 に答える