如何改进使用java泛型编写的实用算法的设计和可读性?

时间:2012-05-09 20:44:03

标签: java algorithm

我发现在使用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, ORANGE2 > 13 > 05 > 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;
    }
}

4 个答案:

答案 0 :(得分:2)

这真的很难看。

IMO在这里使用枚举是错误的。枚举应该是编程常量,而不是人们的偏好。

您应该创建一个PersonFruitPreference类,它使用Map来允许Person在水果上设置首选项。还要添加方法getFavoriteFruit()

答案 1 :(得分:1)

首先使用sortedlist。

其次只需致电:

列出结果; for(T in enummap){   result.add(enummap.get(t)的获得(0)); //假设你做了一个降序排序 }

返回结果。

答案 2 :(得分:1)

如果你需要很多方法都使用相同的泛型类型,我认为你可以将KT的辅助方法放入一个类中,然后只指定完整的泛型整个类的类型。要使用它们,您将创建该类的对象,然后从中调用方法。

该对象将是无状态的,但它为您提供了一种将所有详细程度放在一个地方的语法方法。

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了。 :)但当然不是每家公司都允许改变,学习它需要一段时间。