2013-04-30 15 views
6

私はストリングのリストを持っています。私はダブルを返す関数に基づいて各文字列を評価したいと思います。次に、計算された値に基づいて最初の5つの文字列が必要です。 5未満の場合、私はそれらのすべてを(順番に)欲しい。文字列が化学化合物であり、関数が質量を計算するとします。関数は計算上高価です。私はそれを文字列ごとに一度評価する必要があります。 (私はちょうどかかわらず、ここにデータを作ってるんだ。)マップの最初のN値<K, V>値でソート

H2O => 18.5 
C12H11O22 => 109.1 
HeNe => 32.0 
H2SO4 => 54.37 
HCl => 19.11 
4FeO3 => 82.39 
Xe6 => 281.9 

プログラムは、それぞれの値によって順に並べ、最初の5つの文字列を返す必要があります。このサンプルデータはH20, HCl, HeNe, H2SO4, 4FeO3です。実際には、私は本当に秩序を気にしません。私はちょうど5つの順序で任意の最低必要があります。

私はこれをPerlでどうやってやるのか考えました。それはほんの数行です:

foreach $s (@str) { 
    $strmap{$s} = f($s); 
} 
@sorted = sort { $strmap{$a} <=> $strmap{$b} } keys %strmap; 
return @sorted[0, 4] 

しかし、私はそれをJavaで行う必要があります。そしてそれは私を夢中にさせている。

最初にHashMap<String, Double>を入力してから、Collections.sortをPerlバージョンと同様にカスタムコンパレータを使って投入しようとしました。しかし、Comparatorをスコープすることで、HashMapを参照して値を参照することができなくなりました。

次に、私はTreeMap<String, Double>を試しましたが、キーで並べ替えるだけで、値によってエントリを並べ替えることができませんでした。

私はTreeMap<Double, String>を試しました。同じDoubleのエントリは破棄されます。しかし、同じDoubleにマッピングされるStringの可能性は低いので、私は前に押しました。 TreeMapにエントリを追加することは問題ありませんが、そこから値を抽出しようとする問題に遭遇しました。

TreeMapはsubMapというメソッドを提供しますが、そのパラメータはサブセットを区切るキーです。私は彼らが何であるかを知らない。私はちょうどそれらの最初の5つをしたい。だから私はvaluesメソッドを使ってTreeMapからすべての値を取得しようとしました。それから私はちょうど最初の10を得ることができます。

ArrayList<String> strs = (ArrayList<String>)(treemap.values()); 
return new ArrayList<String>(strs.subList(0, 5)); 

いいえ。ランタイムエラー:TreeMap $値をArrayListにキャストできません。

List<String> strs = (List<String>)(treemap.values()); 
return new ArrayList<String>(strs.subList(0, 5)); 

と同じです。実行時にエラーが発生しました。 OK、

Collection<String> strs = treemap.values(); 
return new ArrayList<String>(strs.subList(0, 5)); 

申し訳ありませんが、subListはコレクションの方法ではありません...ちょうどコレクションに割り当てることができます。

Collection<String> strs = treemap.values(); 
ArrayList<String> a = new ArrayList<String>(strs); 
return new ArrayList<String>(a.subList(0, 5)); 

最後に、機能するものがあります。しかし、最初の5つの要素を取得するための2つの余分なデータ構造はありますか? TreeMapのキーとしてDoubleを使用することについてはあまり野生ではありません。

もっと良い解決策はありますか?

+0

質問をよりよく理解するためのサンプルを提供してください。 – asifsid88

+0

サンプルデータ?または私が試したことのサンプルコード? –

+0

サンプルデータには、入力のセットを指定すると、期待される出力は何ですか? – asifsid88

答えて

3

Javaではなく、上記の3行よりもコンパクトになるとは思いません。

これ以外にもデータ構造としてMapが間違った選択であるという印象があります。文字列検索を必要としないように思われるからです(何らかの形で複数の出現あなたはそう言わなかった)。別のアプローチは、独自の比較可能なデータ・レコード・クラスを宣言するために、次のようになります。

private static class Record implements Comparable<Record> { 
    // public final fields ok for this small example 
    public final String string; 
    public final double value; 

    public Record(String string, double value) { 
     this.string = string; 
     this.value = value; 
    } 

    @Override 
    public int compareTo(Record other) { 
     // define sorting according to double fields 
     return Double.compare(value, other.value); 
    } 
} 

// provide size to avoid reallocations 
List<Record> records = new ArrayList<Record>(stringList.size()); 
for(String s : stringList) 
    records.add(new Record(s, calculateFitness(s)); 
Collections.sort(records); // sort according to compareTo method 
int max = Math.min(10, records.size()); // maximum index 
List<String> result = new ArrayList<String>(max); 
for(int i = 0; i < max; i++) 
    result.add(records.get(i).string); 
return result; 

これは(これはすべての後に、Javaのです)今はるかに冗長上記の3本のラインよりもですが、また、必要となるコードが含まれていますキー/値のペアをマップに挿入します。

+0

これはかなりうまくいっていて、理解しやすいです! –

1

次のような作業はありますか?

データをソートする以外に二重の値を必要としないと仮定しています。

public static void main(String[] args) throws Exception { 
    List<String> data = new ArrayList<>(Arrays.asList("t", "h", "i", "s", "i", "s", "t", "e", "s", "t", "d", "a", "t", "a")); 

    Collections.sort(data, new Comparator<String>() { 
    @Override 
    public int compare(String o1, String o2) { 
     double o1Value = evaluate(o1); 
     double o2Value = evaluate(o2); 
     return Double.compare(o1Value, o2Value); 
    } 
    }); 

    List<String> result = data.subList(0, 10); // Note the end point is exclusive 

    for (String s : result) { 
    System.out.println(s); 
    } 
} 

private static double evaluate(String s) { 
    return s.codePointAt(0); // Nonsense, I know 
} 

この例のプリント:

a 
a 
d 
e 
h 
i 
i 
s 
s 
s 
+0

このアプローチでは、必要以上に 'evaluate()'呼び出しを実行することに注意してください(この関数を呼び出すのが安いか、パフォーマンスはまったく問題にならない場合もあります)。また、 'subList()'によって返されたリストは元のリストに裏打ちされているので、 'data'の内容は' result'への参照が維持されている間ガベージコレクトされません。 – misberner

+0

@polkageistはい、良い点。後者は容易に対処することができます。前者は設計の選択肢です。もし 'evaluate()'が高価な場合、(あなたの例のように)別のクラスを追加する努力は価値があるかもしれません。 –

0

は、なぜあなたは単に計算を行いStringDoubleと機能を組み合わせるためにクラスを作成していない - のようなもの:

public Thing implements Comparable<Thing> 
{ 
    private String s; 
    private Double d; 

    public Thing(String s) 
    { 
    this.s = s; 
    this.d = calculateDouble(s); 
    } 

    public String getString() 
    { 
    return this.s; 
    } 

    public Double getDouble() 
    { 
    return this.d; 
    } 

    public int compareTo(Thing other) 
    { 
    return getDouble().compareTo(other.getDouble()); 
    } 

    public Double calculateDouble(String s) 
    { 
    ... 
    } 
} 

そして、あなたが必要とするのは、List<Thing>,Collections.sortList.subListです。

関連する問題