Java 仅迭代地图的一部分

Java 仅迭代地图的一部分,java,data-structures,indexing,hashmap,iteration,Java,Data Structures,Indexing,Hashmap,Iteration,我将数据存储在HashMap中,我希望通过多个线程同时访问该HashMap,以分割对项目所做的工作 通常情况下(以列表为例),我会给每个线程一个索引作为开始,并可以像这样轻松地拆分工作: for(int i = startIndex; i < startIndex+batchSize && i < list.size(); i++) { Item a = list.get(i); // do stuff with the Item } for(int

我将数据存储在HashMap中,我希望通过多个线程同时访问该HashMap,以分割对项目所做的工作

通常情况下(以列表为例),我会给每个线程一个索引作为开始,并可以像这样轻松地拆分工作:

for(int i = startIndex; i < startIndex+batchSize && i < list.size(); i++)
{
    Item a = list.get(i);
    // do stuff with the Item
}
for(int i=startIndex;i
当然,这不适用于HashMap,因为我无法通过索引访问它

有没有一种简单的方法可以只迭代地图的一部分?我应该为这个案例使用另一个数据结构吗

我读过关于SortedMap的文章,但它有太多我不需要的开销(对项目进行排序)。我有很多数据,性能至关重要


任何提示都将不胜感激。

如果您只进行了几次遍历,或者如果地图没有更改,您可以获得一组密钥,然后将其发送到阵列。从这里开始,这几乎是你的常规方法。但是很明显,如果HashMap发生了变化,那么您将不得不重新执行这两个操作,这可能会非常昂贵。

首先,您不应该使用HashMap,因为迭代顺序是未定义的。使用迭代顺序与插入顺序相同的
LinkedHashMap
,或者使用迭代顺序为自然排序顺序的
TreeMap
。我建议使用LinkedHashMap,因为插入一个条目会使地图的切片变得不可预测

要分割地图,请使用以下代码:

    LinkedHashMap<Integer, String> map = new LinkedHashMap<Integer, String>();

    for (Map.Entry<Integer, String> entry : new ArrayList<Map.Entry<Integer,String>>(map.entrySet()).subList(start, end)) {
        Integer key = entry.getKey();
        String value = entry.getValue();
        // Do something with the entry
    }
LinkedHashMap=新建LinkedHashMap();
for(Map.Entry:newarraylist(Map.entrySet()).subList(start,end)){
整数key=entry.getKey();
字符串值=entry.getValue();
//对条目做些什么
}
我已经在代码中添加了行,但扩展后的代码相当于:

List<Map.Entry<Integer, String>> entryList = new ArrayList<Map.Entry<Integer,String>>();
entryList.addAll(map.entrySet());
entryList = entryList.subList(start, end); // You provide the start and end index
for (Map.Entry<Integer, String> entry : entryList) ...
List entryList=new ArrayList();
addAll(map.entrySet());
entryList=entryList.subList(开始、结束);//您可以提供开始索引和结束索引
对于(Map.Entry:entryList)。。。
使用HashMap#keySet->Set#数组,您将获得一个键数组


使用这个数组,您可以像以前一样进行处理,保留密钥数组并将它们传递给线程。然后每个线程将只访问分配给它的键,最后您可以只使用这些键访问HashMap的给定分区的条目

除非你的映射是巨大的,否则在映射上迭代的成本与在另一个线程上启动任务的成本相比是很小的,与你打算做的工作相比是微不足道的

因此,划分工作的最简单方法可能是将地图转换为数组并将其分解

final Map<K, V> map =
final ExecutorServices es = 
final int portions = Runtime.getRuntime().availableProcessors();
final Map.Entry<K,V>[] entries = (Map.Entry<K,V>[]) map.entrySet().toArray(new Map.Entry[map.size()]);
final int portionSize = (map.size() + portions-1)/ portions;

for(int i = 0; i < portions; i++) {
    final int start = i * portionSize;
    final int end = Math.min(map.size(), (i + 1) * portionSize);
    es.submit(new Runnable() {
        public void run() {
            for(int j=start; j<end;j++) {
               Map.Entry<K,V> entry = entries[j];
               // process entry.
            }
        }
    });
}
最终地图=
最终遗嘱执行人=
final int partions=Runtime.getRuntime().availableProcessors();
final Map.Entry[]entries=(Map.Entry[])Map.entrySet().toArray(new Map.Entry[Map.size());
最终int-portionSize=(map.size()+部分-1)/部分;
for(int i=0;i<部分;i++){
最终int start=i*portionSize;
final int end=Math.min(map.size(),(i+1)*portionSize);
es.submit(新的Runnable(){
公开募捐{

对于(int j=start;j您希望映射如何分区?不确定我是否得到了问题:)我希望映射分区为与我拥有的线程数相同的部分(例如8个)。如果可能的话,分区不应该是一个代价高昂的操作。atm我正在处理100000个条目,但它确实是开放式的。该算法也应该可以很好地扩展到更大的大小。幸运的是,哈希映射不会被线程更改。假设toArray()方法很便宜。我将尝试一下,看看性能有多好,干杯。TreeMap不是一个选项,因为项目的顺序将是一个performancekiller,我不需要项目的特殊顺序。如果地图在我使用它时没有改变,我还应该使用LinkedHashMap吗?我不关心项目的顺序,为什么定义它很重要吗?感谢entryList提供的解决方案。任何人都会将其与Ross Larson的想法进行比较,看看什么执行得更快。:)因为如果你在一个线程中要求项目1到5,在另一个线程中要求项目6到10,你可以在这两个线程中得到相同的项目-hashmap没有定义迭代顺序(虽然它在现实中可能是固定的-你可以尝试一下)已经这样做了,而且它看起来是固定的。但是你是对的,未定义的行为从来都不是一件好事。LinkedHashMap是!谢谢!我在考虑这个问题之前也不知道:)这是关于SO的伟大之处——你在思考一个问题时学到了很多东西。我就像“嗯,如果Set有一个toArray呢?”——检查了JavaDoc——它有:)