Java - Google Guava Multimap问题

时间:2013-12-11 07:09:41

标签: java guava multimap

我已经实现了类似LFU的Web缓存替换算法,其工作方式如下:在每个请求中,我分配一个由1 /(p ^ reqIndex)给出的权重,其中p是权重因子,reqIndex是请求的索引(第1次请求,第2次请求等)。例如,当p = 0.5时,第1次请求的权重为1,第2次请求为2,第3次为4等。然后为了计算每个请求项目的得分(我称之为加权频率,weightFreq),我只是加上相应的权重。请求的项目仅通过我称为请求ID(reqID)的数字ID(整数)来区分。例如,如果第一个请求是针对ID为7的项目,第二个针对ID为3的项目,而第三个请求针对ID为7的项目,则ID为7的项目应为1 + 4 = 5 (第1次请求的权重+第3次请求的权重),ID为3的项目的得分为2(第2次请求的权重)。

我使用ListMultimap(通过Google Guava库)来获得权重,因为我应该能够为单个键(reqID)提供多个值(权重):

/** ListMultimap of reqID (K) and weights (V) */
private ListMultimap<Integer, Double> weights;

我使用简单的地图分数:

/** Map of reqID (K) and weightFreq (V) */
private Map<Integer, Double> weightFreqs;

这是getter方法,我计算并返回所请求项目的分数:

/**
 * Getter for weighted frequency of the requested item
 * @param   request                             The requested item
 * @param   reqIndex                            The index of the request
 * @return  this.weightFreqs.get(request.reqID) weightFreq of the req. item
 */
public double getWeightFreq(Request request, int reqIndex) {

    // initialize weightFreq
    initWeightFreqs(request);

    // calculate weight
    weight = Math.pow(1/p, reqIndex);

    // put request along with its weight to the weights ListMultimap
    weights.put(request.reqID, weight);

    // scan keys of weights list-multimap
    for(Integer key : this.weights.keySet()) {

        // if the key is the requested item
        if (key == request.reqID) {

            // list of all weights for this key (reqID)
            List<Double> tempWeights = weights.get(key);

            // test
            logger.info("List of weights for request with ID: " +   
                            request.reqID + " is: " + tempWeights);

            // sum of those weights
            int sum = 0;

            // scan the list
            for(int i = 0; i < tempWeights.size(); i++) {

                // sum the weights
                sum += tempWeights.get(i);

            }

            // assign the sum to the weightFreq value
            weightFreq = sum;

            // put reqID along with its weightFreq into weightFreqs map
            weightFreqs.put(request.reqID, weightFreq);

        }

    }

    // return weightFreq of requested item
    return this.weightFreqs.get(request.reqID);

}

如您所见,我包含了测试打印(每个reqID的权重列表)以检查代码。现在看看运行代码时会发生什么。同样的例子,第一个请求是reqID 7,第二个请求是reqID 3.第一个请求后我得到:

请求:7,权重:[1.0]

没关系。但在第二次请求后我得到了:

需求:7,重量:[1.0] 请求:3,权重:[1.0,2.0]

哪个错了!正确的应该是:

需求:7,重量:[1.0] 请求:3,权重:[2.0]

因此,我得到reqID 3的得分(weightFreq)等于3(1 + 2)而不是2,这是正确的。

请注意,我仅在过去7个月左右的时间内学习编程,这是我第一次使用multimap(因为我需要为单个键分配多个值)。我从这里得到了这样做的想法和相关信息:

http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/collect/Multimap.html

它描述了多图的几种不同实现(ListMultimap,SetMultimap等),可以选择使用集合图或asMap通过Google Guava等来做同样的事情。

我不确定我是否理解了有关多重映射的错误,或者我的逻辑中有关于之前的getter方法的错误。任何指导将不胜感激。

2 个答案:

答案 0 :(得分:1)

不知道这里发生了什么,但是评论太多了。你可以肯定地修复它,只是先简化它。测试

key == request.reqID

很可能是错误的,因为您处理的是Integer而不是int。在这种情况下,永远不要依赖自动(联合)拳击并使用equalsintValue

您的代码过于复杂。你想处理request.reqID的条目,所以得到它们:

List<Double> tempWeights = weights.get(request.reqID);

删除外部循环和if

简化更多,例如,使用foreach循环

for (double d : tempWeights) sum += d;

当所有不必要的东西都消失了,你可能会立即看到问题。

答案 1 :(得分:0)

您可能在多线程环境中工作。看起来你可以简化代码,节省内存,并通过引入:

使你的计算成为原子
ConcurrentHashMap<Integer, RequestInfo> map = new ConcurrentHashMap<>();

class RequestInfo{
    double weight;
    List<Double> frequencies;  // consider using TDoubleArrayList from Trove4j
                               // for efficiency
    public synchronized addFrequency(double freq){
        ...
    }
}

然后使用map.putIfAbsent进行空RequestInfo的原子插入。