我有一个字符串列表。我想基于返回double的函数来评估每个字符串。然后我想要前5个字符串,基于他们的计算值。如果少于5,我想要所有这些(按顺序)。假设字符串是化学化合物,函数计算质量。该功能在计算上很昂贵;我需要每串评估一次。 (不过我只是在这里编写数据。)
H2O => 18.5
C12H11O22 => 109.1
HeNe => 32.0
H2SO4 => 54.37
HCl => 19.11
4FeO3 => 82.39
Xe6 => 281.9
程序应返回按其各自值排列的前五个字符串。对于此示例数据:H20, HCl, HeNe, H2SO4, 4FeO3
。实际上,我并不关心订单;我只需要任何顺序的五个最低点。
我想过如何在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版本一样。但是比较器的范围使它无法引用HashMap来查找值。
然后我尝试了一个TreeMap<String, Double>
,但它只按键进行排序,没有任何强制可以让它按值排序。
所以我尝试了TreeMap<Double, String>
。它将丢弃具有相同Double的条目。但是,将字符串映射到同一个Double的可能性很低,所以我向前推进。将条目添加到TreeMap没有问题,但是我遇到了试图从中提取值的问题。
TreeMap提供了一个名为subMap
的方法,但其参数是用于分隔子集的键。我不知道它们是什么;我只想要前五个。所以我尝试使用values
方法从TreeMap中获取所有值,希望它们按顺序排列。然后我就可以得到前十名。
ArrayList<String> strs = (ArrayList<String>)(treemap.values());
return new ArrayList<String>(strs.subList(0, 5));
不。运行时错误:无法将TreeMap $ Values转换为ArrayList。
List<String> strs = (List<String>)(treemap.values());
return new ArrayList<String>(strs.subList(0, 5));
相同。尝试执行强制转换的运行时错误。好的,我们只是分配给一个集合......
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));
最后,有效的东西!但两个额外的数据结构只是为了获得前五个元素?而且我并不太喜欢使用Double作为TreeMap的关键。
有更好的解决方案吗?
答案 0 :(得分:3)
我认为你不会比上面的三行更紧凑,而不是Java。
除此之外,我的印象是首先将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)要冗长得多,但也包括将键/值对插入地图所需的代码。
答案 1 :(得分: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
答案 2 :(得分:0)
为什么不创建一个类来组合String
,Double
和执行计算的函数 - 类似于:
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.sort
和List.subList
。