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;
}