我发现在使用java集合时,特别是在使用泛型编写实用程序方法时,我的代码通常很丑陋,充满了空检查,嵌套循环和重复。关注这个例子,我想要改进的想法。
假设我们有一个EnumMap
,其值是评级列表。例如,假设enum
本身代表水果,每个值代表不同人给出的列表评级。
APPLE -> [1, 3, 4]
ORANGE -> [2, 0, 5]
John rated apple 1, Mary rated apple 3, Steve rated apple 4
John rated orange 2, Mary rated orange 0, Steve rated orange 5
Note the specific names are irrelevant and provided only to clarify the setup
现在我们要编写一个实用方法,接受上面的数据结构,并返回每个人最喜欢的水果列表。因此,上述示例数据的预期结果为[ORANGE, APPLE, ORANGE
,2 > 1
,3 > 0
和5 > 4
。
以下是我目前执行此操作的方法。我想要一种同样(或更多)有效但更清晰的方法来编写相同的算法。
谢谢!
public class MyListUtil {
public static <K extends Enum<K>, T extends Object & Comparable<? super T>> List<K> maxKeysByIndex(EnumMap<K, List<T>> enumMap) {
Iterator<K> keysIter = enumMap.keySet().iterator();
int sizeOfAllLists = enumMap.get(keysIter.next()).size();
List<K> ret = new ArrayList<K>();
for (int i=0; i<sizeOfAllLists; i++) {
keysIter = enumMap.keySet().iterator();
K maxIndexKey = null;
T maxIndexVal = null;
while (keysIter.hasNext()){
K curKey = keysIter.next();
T curVal = enumMap.get(curKey).get(i);
if (maxIndexVal == null || curVal.compareTo(maxIndexVal) > 0) {
maxIndexVal = curVal;
maxIndexKey = curKey;
}
}
ret.add(maxIndexKey);
}
return ret;
}
}
答案 0 :(得分:2)
这真的很难看。
IMO在这里使用枚举是错误的。枚举应该是编程常量,而不是人们的偏好。
您应该创建一个PersonFruitPreference类,它使用Map来允许Person在水果上设置首选项。还要添加方法getFavoriteFruit()
答案 1 :(得分:1)
首先使用sortedlist。
其次只需致电:
列出结果; for(T in enummap){ result.add(enummap.get(t)的获得(0)); //假设你做了一个降序排序 }
返回结果。
答案 2 :(得分:1)
如果你需要很多方法都使用相同的泛型类型,我认为你可以将K
和T
的辅助方法放入一个类中,然后只指定完整的泛型整个类的类型。要使用它们,您将创建该类的对象,然后从中调用方法。
该对象将是无状态的,但它为您提供了一种将所有详细程度放在一个地方的语法方法。
public class <K extends Enum<K>, T extends Object & Comparable<? super T>> MyListUtil {
public List<K> maxKeysByIndex(EnumMap<K, List<T>> enumMap) {
...
//other methods
}
您可以尝试将内部循环放入单独的方法中,例如:
public K getMaxKeyFromPos(EnumMap<K, List<T>> enumMap, int pos)
{
K maxIndexKey = null;
T maxIndexVal = null;
for (K curKey : enumMap.keySet()) {
T curVal = enumMap.get(curKey).get(pos);
if (maxIndexVal == null || curVal.compareTo(maxIndexVal) > 0) {
maxIndexVal = curVal;
maxIndexKey = curKey;
}
}
return maxIndexKey;
}
我还将其更改为for-each
语法,删除了一些迭代器。
答案 3 :(得分:1)
宣传Scala的绝佳机会。您可能知道,Scala在JVM上运行,并且与Java字节码完全兼容。它自己编译为JVM字节码。
这个精简的工作代码并不明显:
val apple = List (1, 3, 4)
val orange = List (2, 0, 5)
val persons = List ("John", "Mary", "Steve")
val prefs = apple.zip (orange) .zip (persons)
// List[((Int, Int), java.lang.String)] = List(((1,2),John), ((3,0),Mary), ((4,5),Steve))
prefs.map (e => e._2 + ": " + (if (e._1._1 > e._1._2) "apple" else "orange"))
// List[java.lang.String] = List(John: orange, Mary: apple, Steve: orange)
是,你有完整的静态编译时安全性。但是在可能的情况下可以推断出类型,因此你的样板更少。
前3行产生3个列表。 然后他们被压缩 - 在你看到的评论中,推断者所说的类型是什么。
这部分有点神秘:
(e => e._2 + ": " + (if (e._1._1
Prefs是((Pair of Int),String)的列表,e是列表中的一个元素。 e._2是字符串部分(出于某些原因,在这样的元组中我们不计算从0开始,但从1开始 - 我猜,因为从元组起源的地方,有这个习惯)而列表和数组从0开始计数也像Java一样。
e._1是元素的第一部分,即一对Ints,它们是水果的首选。 e._1._1为第一个,e._1._2为第二个水果。
在使用Scala集合一段时间之后,我不再喜欢Java了。 :)但当然不是每家公司都允许改变,学习它需要一段时间。