Java 提高列表和地图的合成速度

Java 提高列表和地图的合成速度,java,optimization,arraylist,hashmap,Java,Optimization,Arraylist,Hashmap,我使用一个Dico类来存储术语的权重和出现的文档id public class Dico { private String m_term; // term private double m_weight; // weight of term private int m_Id_doc; // id of doc that contain term public Dico(int Id_Doc,String Term,double tf_ief ) {

我使用一个
Dico
类来存储术语的权重和出现的文档id

public class Dico 
{
   private String m_term; // term
   private double m_weight; // weight of term
   private int m_Id_doc; // id of doc that contain term

   public Dico(int Id_Doc,String Term,double tf_ief ) 
   {
      this.m_Id_doc = Id_Doc;
      this.m_term = Term;
      this.m_weight = tf_ief;
   }
   public String getTerm()
   {
      return this.m_term;
   }

   public double getWeight()
   {
     return this.m_weight;
   }

   public void setWeight(double weight)
   {
     this.m_weight= weight;
   }

   public int getDocId()
   {
     return this.m_Id_doc;
   }                
}
我用这个方法从
地图
列表
计算最终权重:

公共列表合并列表映射(列表列表,映射映射)
{
//在地图中,每个术语都是唯一的,但在列表中我有冗余
List\u term\u weight=new ArrayList();
对于(Map.Entry entrySet:Map.entrySet())
{
String key=entrySet.getKey();
Double value=entrySet.getValue();
用于(Dico dic:列表)
{    
字符串项=dic.getTerm();
双倍重量=dic.getWeight();
if(键等于(项))
{
双倍新重量=重量*值;
添加(新Dico(dic.getDocId(),term,new_weight));
}                  
} 
}
返回列表\术语\权重;
}
我在地图中有36736个元素,在列表中有1053914个元素,目前这个程序需要花费大量时间编译:
构建成功
(总时间:17分15秒)


如何仅从列表中获取与map中的术语相等的术语?

对于
list
,应使用
contains()
方法。这样,您就可以避免使用的第二个
。即使
contains()
方法具有O(n)复杂性,您也应该看到一个小小的改进。当然,请记住重新实现
equals()
。否则,您应该按照bot的建议使用第二个
映射。

您可以使用映射的查找功能,即Map.get(),因为您的映射将术语映射到权重。这应该有显著的性能改进。唯一的区别是输出列表的顺序与输入列表相同,而不是关键点在权重映射中出现的顺序

public List<Dico> merge_list_map(List<Dico> list, Map<String, Double> map)
{
    // in map each term is unique but in list i have redundancy
    List<Dico> list_term_weight = new ArrayList<>();

    for (Dico dic : list)
    {
        String term = dic.getTerm();
        double weight = dic.getWeight();

        Double value = map.get(term);  // <== fetch weight from Map
        if (value != null)
        {
            double new_weight = weight * value;

            list_term_weight.add(new Dico(dic.getDocId(), term, new_weight));

        }
    }
    return list_term_weight;
}
定时测试-10000个元件
List List=new ArrayList();
映射权重=新HashMap();
对于(int i=0;i<1e4;i++){
添加(新的Dico(i,“foo-”+i,i));
如果(i%3==0){

放置(“foo-”+i,(double)i);//此外,检查映射的初始化。()映射的重新刷新在性能上代价高昂

作为一般规则,默认负载系数(.75)提供了良好的性能 时间和空间成本之间的折衷。值越高,成本越低 空间开销,但会增加查找成本(反映在大多数 HashMap类的操作,包括get和put)。预期 应考虑映射中的条目数及其负载系数 在设置其初始容量时使用帐户,以便将 再灰化操作次数。如果初始容量大于 最大条目数除以负载系数,无重新灰分 行动永远不会发生

如果要在HashMap实例中存储多个映射,请创建它 具有足够大的容量将允许映射 存储的效率比让它根据需要执行自动重新灰化更高 需要增加桌子的面积

如果您知道地图中放置的元素数量,或者有一个近似值,则可以按如下方式创建地图:

Map<String, Double> foo = new HashMap<String, Double>(maxSize * 2);
Map foo=newhashmap(maxSize*2);

根据我的经验,您可以将性能提高2倍或更多。

使用映射的查找功能,如Adam所指出的,并使用HashMap作为映射的实现—HashMap查找复杂性为O(1)。这将提高性能。

为了使
合并列表映射
功能更高效,您需要实际使用
映射
,它是一种高效的键查找数据结构。 当您在
映射
条目上循环并在
列表
中查找匹配项时,算法是O(N*M),其中M是映射的大小,N是列表的大小。这肯定是您能得到的最差结果

如果首先循环查看
列表
,然后针对每个
术语
,使用
Map$get(String key)
Map
中进行查找,您将得到O(N)的时间复杂度,因为Map查找可以被视为O(1)

在设计方面,如果您可以使用Java8,您的问题可以用
Stream
s来翻译:

public static List<Dico> merge_list_map(List<Dico> dico, Map<String, Double> weights) {
    List<Dico> wDico = dico.stream()
            .filter  (d -> weights.containsKey(d.getTerm()))
            .map     (d -> new Dico(d.getTerm(), d.getWeight()*weights.get(d.getTerm())))
            .collect (Collectors.toList());
    return wDico;
}
由于这本书中只有10万个单词,为了更好地衡量,只有x10个单词(
initDico()
是从这些单词构建
列表的助手):

将1M单词与权重图合并的测试:

long start = System.currentTimeMillis();
List<Dico> wDico = merge_list_map(bigDico, weights);
long end = System.currentTimeMillis();
System.out.println("===== Elapsed time (ms): "+(end-start)); 
// => 105 ms

使用两个映射,而不是一个映射和一个列表。你如何初始化映射?你有列表中的所有术语吗?还是它是一个子集?你说的是编译时和构建成功,尽管你的问题显然是运行时的问题。你能确认吗?是的,因为下一步是使用这个术语对节点wi进行分类th SOM neuronal netmaxSize*2的大小可能有点过大,但会减少冲突并提供更好的性能。使用默认负载因子,如果您知道确切的最大大小,则所需的最小大小为maxSize*4/3+1。感谢您的帮助,但我必须使用所有术语来解释映射条款,并在map.term=dic.ter时更新权重m.我对预期的输入和输出有点困惑,你们能用小的工作示例值更新问题吗,我相信我所做的和我的测试结果是一样的。好的,我很困惑,因为我说你们的解决方案非常有用,谢谢
List<Dico> list = new ArrayList<Dico>();
Map<String, Double> weights = new HashMap<String, Double>();
for (int i = 0; i < 1e4; i++) {
    list.add(new Dico(i, "foo-" + i, i));
    if (i % 3 == 0) {
        weights.put("foo-" + i, (double) i);  // <== every 3rd has a weight
    }
}

long t0 = System.currentTimeMillis();
List<Dico> result1 = merge_list_map_original(list, weights);
long t1 = System.currentTimeMillis();
List<Dico> result2 = merge_list_map_fast(list, weights);
long t2 = System.currentTimeMillis();

System.out.println(String.format("Original: %d ms", t1 - t0));
System.out.println(String.format("Fast:     %d ms", t2 - t1));

// prove results equivalent, just different order
// requires Dico class to have hashCode/equals() - used eclipse default generator
System.out.println(new HashSet<Dico>(result1).equals(new HashSet<Dico>(result2)));
Original: 1005 ms
Fast:     16 ms  <=== loads quicker
true
Map<String, Double> foo = new HashMap<String, Double>(maxSize * 2);
public static List<Dico> merge_list_map(List<Dico> dico, Map<String, Double> weights) {
    List<Dico> wDico = dico.stream()
            .filter  (d -> weights.containsKey(d.getTerm()))
            .map     (d -> new Dico(d.getTerm(), d.getWeight()*weights.get(d.getTerm())))
            .collect (Collectors.toList());
    return wDico;
}
String text = null;
try (InputStream url = new URL("http://www.gutenberg.org/files/2149/2149-h/2149-h.htm").openStream())  {
    text = new Scanner(url, "UTF-8").useDelimiter("\\A").next();    
}
String[] words = text.split("[\\p{Punct}\\s]+");
System.out.println(words.length); // => 108028
List<Dico> dico = initDico(words);
List<Dico> bigDico = new ArrayList<>(10*dico.size());
for (int i = 0; i < 10; i++) {
    bigDico.addAll(dico);
}
System.out.println(bigDico.size()); // 1080280
Map<String, Double> weights = initWeights(words);
System.out.println(weights.size()); // 9449 distinct words
long start = System.currentTimeMillis();
List<Dico> wDico = merge_list_map(bigDico, weights);
long end = System.currentTimeMillis();
System.out.println("===== Elapsed time (ms): "+(end-start)); 
// => 105 ms
private static List<Dico> initDico(String[] terms) {
    List<Dico> dico = Arrays.stream(terms)
            .map(String::toLowerCase)
            .map(s -> new Dico(s, 1.0))
            .collect(Collectors.toList());
    return dico;
}

// weight of a word is the frequency*1000
private static Map<String, Double> initWeights(String[] terms) {
    Map<String, Long> wfreq = termFreq(terms);
    long total = wfreq.values().stream().reduce(0L, Long::sum);
    return wfreq.entrySet().stream()
            .collect(Collectors.toMap(Map.Entry::getKey, e -> (double)(1000.0*e.getValue()/total)));
}

private static Map<String, Long> termFreq(String[] terms) {
    Map<String, Long> wfreq = Arrays.stream(terms)
            .map(String::toLowerCase)
            .collect(groupingBy(Function.identity(), counting()));
    return wfreq;
}