如何遍历哈希表?

597

我有一个字段:

HashMap<String, HashMap> selects = new HashMap<String, HashMap>();

对于每一个 Hash<String, HashMap>,我需要创建一个 ComboBox,其项是 HashMap <String, **HashMap**> 的值(它本身也是一个哈希映射表)。

以下是(不工作的)演示:

for (int i=0; i < selects.size(); i++) {
    HashMap h = selects[i].getValue();
    ComboBox cb = new ComboBox();

    for (int y=0; y < h.size(); i++) {
        cb.items.add(h[y].getValue);
    }
}

3
在Java 8中使用Lambda表达式:https://dev59.com/zW855IYBdhLWcg3woV0_#25616206 - Nitin Mahesh
Java 8使用流:https://dev59.com/zW855IYBdhLWcg3woV0_#32343195,流的一个优点是它们也可以并行化。 - akhil_mittal
7个回答

1403

我知道我有点晚了,但我也想分享一下我做了什么,以防对其他人有帮助:

HashMap<String, HashMap> selects = new HashMap<String, HashMap>();

for(Map.Entry<String, HashMap> entry : selects.entrySet()) {
    String key = entry.getKey();
    HashMap value = entry.getValue();

    // do what you have to do here
    // In your case, another loop.
}

128
我总是记不住如何写这个,所以我总是回到这个相同的答案。点赞,因为这是一个干净的答案,而且这段代码被复制粘贴到我的许多项目中。谢谢! - Katu
16
请写下"yourmap..entrySet().for",IDE将为您自动完成。意思是让您在代码中输入该语句,并由IDE自动完成相应的代码补全。 - AITAALI_ABDERRAHMANE

295

Java 8 中的Lambda表达式

在 Java 1.8(Java 8)中,通过使用来自聚合操作(流操作)的 forEach 方法,这变得更加容易,它看起来类似于从 Iterable 接口的迭代器。

只需将下面的语句复制粘贴到您的代码中,并将 HashMap 变量从 hm 重命名为您的 HashMap 变量以打印出键值对。

HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
/*
 *     Logic to put the Key,Value pair in your HashMap hm
 */

// Print the key value pair in one line.
hm.forEach((k,v) -> System.out.println("key: "+k+" value:"+v));

在这里有一个使用 Lambda 表达式 的示例:
    HashMap<Integer,Integer> hm = new HashMap<Integer, Integer>();
    Random rand = new Random(47);
    int i=0;
    while(i<5){
        i++;
        int key = rand.nextInt(20);
        int value = rand.nextInt(50);
        System.out.println("Inserting key: "+key+" Value: "+value);
        Integer imap =hm.put(key,value);
        if( imap == null){
            System.out.println("Inserted");
        }
        else{
            System.out.println("Replaced with "+imap);
        }               
    }

    hm.forEach((k,v) -> System.out.println("key: "+k+" value:"+v));

Output:

Inserting key: 18 Value: 5
Inserted
Inserting key: 13 Value: 11
Inserted
Inserting key: 1 Value: 29
Inserted
Inserting key: 8 Value: 0
Inserted
Inserting key: 2 Value: 7
Inserted
key: 1 value:29
key: 18 value:5
key: 2 value:7
key: 8 value:0
key: 13 value:11

同时,也可以使用Spliterator来实现同样的功能。

Spliterator sit = hm.entrySet().spliterator();

更新


包括指向 Oracle 文档的文档链接。 有关 Lambda 的更多信息,请转到此链接,并必须阅读聚合操作以及有关 Spliterator 的信息,请转到此链接


1
我猜我们不能在封闭作用域内使用局部变量而不将它们声明为final。当在lambda体外声明变量并在lambda中使用时,会出现“在封闭作用域中定义的局部变量schedule必须是final或有效final”的错误,这与Groovy中的闭包不同。 - Mahesha999
5
使用lambda的缺点是,你不能在lambda内部返回另一个方法。 - apscience
这里的Spliterator有什么用?你在使用并行流吗? - akhil_mittal
如果您正在开发Android应用程序,此处提到的forEach调用需要API级别24。 - A.J.

55

Map.values():

HashMap<String, HashMap<SomeInnerKeyType, String>> selects =
    new HashMap<String, HashMap<SomeInnerKeyType, String>>();

...

for(HashMap<SomeInnerKeyType, String> h : selects.values())
{
   ComboBox cb = new ComboBox();
   for(String s : h.values())
   {
      cb.items.add(s);
   }
}

2
+1:这是比我的答案更整洁的方法(假设我们使用的是Java 5或更高版本)。 - Oliver Charlesworth
1
在外部HashMap中使用通用参数似乎表明是Java 5或更高版本。但我对这是哪个Java版本有疑问,因为内部的HashMap没有通用参数,并且对HashMap进行了数组访问...但这只是一些“伪”代码来传达问题。 - Bert F
1
能否解释一下为什么它对你似乎没有起作用?你使用的是Java 5或更高版本吗?我们正在按照您的代码示例进行操作,但似乎没有对组合框进行任何操作。或者值的顺序不是您想要的吗?还是您需要组合框中的键而不是值?我认为大多数人都会同意前三个答案都可以正常工作,因此很可能存在应用答案或其他问题的问题,如果您提供更多信息,我们可能能够帮助解决。 - Bert F
1
哦哦哦...我太蠢了 =)。只有一个问题,如何获取HashMap<this->String,HashMap<SomeInnerKeyType,String>>? - Mediator
1
@simply denis - 很抱歉,我不明白你的问题?你是在问如何获取对 HashMap<String,HashMap<SomeInnerKeyType,String>> 的引用吗?如果你的循环在一个方法中,你应该能够使用 this.selects 或者简单地使用 selects?否则,你应该将 map 作为参数传递给包含循环的方法。或者你是在问如何获取给定键(this->String?)的特定内部 HashMap<SomeInnerKeyType,String> 吗?你可以使用 selects.get(keyString)。如果我完全误解了,请澄清一下。 - Bert F
1
为了得到更好的答案,请参考下面 Cyril N. 的回答。 - Zack Marrapese

33

Java 8中的流

除了接受lambda表达式forEach方法,我们还拥有Java 8中的流API

遍历条目(使用forEach和Streams):

sample.forEach((k,v) -> System.out.println(k + "=" + v)); 
sample.entrySet().stream().forEachOrdered((entry) -> {
            Object currentKey = entry.getKey();
            Object currentValue = entry.getValue();
            System.out.println(currentKey + "=" + currentValue);
        });
sample.entrySet().parallelStream().forEach((entry) -> {
            Object currentKey = entry.getKey();
            Object currentValue = entry.getValue();
            System.out.println(currentKey + "=" + currentValue);
        });

使用流的优势在于它们可以轻松并行化,并且在拥有多个 CPU 时非常有用。我们只需要在上面的 stream() 中使用 parallelStream() 即可。使用并行流更有意义的是使用 forEach,因为 forEachOrdered 在性能上没有任何区别。如果我们想要遍历键,可以使用 sample.keySet(),对于值,可以使用 sample.values()为什么要使用带有 streams 的 forEachOrdered 而不是 forEach 流也提供了 forEach 方法,但是 forEach 的行为是明确非确定性的,而 forEachOrdered 对于该流的每个元素执行一个操作,如果该流具有定义的遇到顺序,则按照该顺序进行。因此,forEach 不能保证顺序被保留。另请参见 this

23

您可以使用迭代器来遍历HashMap(以及许多其他集合),例如:

HashMap<T,U> map = new HashMap<T,U>();

...

Iterator it = map.values().iterator();

while (it.hasNext()) {
    System.out.println(it.next());
}

2
我无法接受这样的设计降低到以下级别。 - Mediator
3
可以的。您可以基于it.next()创建一个内部迭代器。 - Oliver Charlesworth

15

我通常做的和cx42net一样,但我不会明确地创建一个Entry。

HashMap<String, HashMap> selects = new HashMap<String, HashMap>();
for (String key : selects.keySet())
{
    HashMap<innerKey, String> boxHolder = selects.get(key);
    ComboBox cb = new ComboBox();
    for (InnerKey innerKey : boxHolder.keySet())
    {
        cb.items.add(boxHolder.get(innerKey));
    }
}

对我来说,这似乎是最直观的方法,我想我对迭代地遍历映射的值有偏见。


如果你正在遍历整个映射,请不要进行查找。每次哈希查找可能是O(1),但现在你正在执行n*O(1)次,总共需要的哈希查找次数为0,因此时间复杂度为O(N)。 - Azeroth2b

9
使用 entrySet
/**
 *Output: 
D: 99.22
A: 3434.34
C: 1378.0
B: 123.22
E: -19.08

B's new balance: 1123.22
 */

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class MainClass {
  public static void main(String args[]) {

    HashMap<String, Double> hm = new HashMap<String, Double>();

    hm.put("A", new Double(3434.34));
    hm.put("B", new Double(123.22));
    hm.put("C", new Double(1378.00));
    hm.put("D", new Double(99.22));
    hm.put("E", new Double(-19.08));

    Set<Map.Entry<String, Double>> set = hm.entrySet();

    for (Map.Entry<String, Double> me : set) {
      System.out.print(me.getKey() + ": ");
      System.out.println(me.getValue());
    }

    System.out.println();

    double balance = hm.get("B");
    hm.put("B", balance + 1000);

    System.out.println("B's new balance: " + hm.get("B"));
  }
}

在这里查看完整示例:

2
它肯定有效,我每天都在使用 :) 到底哪里不起作用?因为你有 HashMap<String, HashMap>,所以你需要两个循环——一个循环外部哈希映射,另一个循环内部哈希映射。顺便说一下 - 你应该明确第二个哈希映射的类型 - 不知道你存储了什么,但像 HashMap<String,HashMap<string,String>> 这样的东西,虽然根据你的示例,似乎应该使用 HashMap<String,Set<String>>。还有一件事 - 尽可能使用接口:当你可以时使用 Map<String,Map> - icyrock.com

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接