Java集合篇-知识整理

目录

集合概述

集合框架

1. Collection接口

2. List接口

2.1 ArrayList

2.2 Vector

2.2.1 Vector 类和 ArrayList 类的区别

2.3 LinkedList

2.3.1 LinkedList 类和 ArrayList 类的比较

2.4 Stack

3. Set 接口

3.1 散列存放:HashSet

3.2 LinkedHashSet

3.3 排序的子类:TreeSet

3.3.1 排序的说明

3.3.2 关于重复元素的说明

4. Map 接口

4.1 新的子类:HashMap

4.2 旧的子类:Hashtable

4.2.1 HashMap 与 Hashtable 的区别

4.3 排序的子类:TreeMap

5. 集合输出

5.1 Iterator

5.2 ListIterator

5.3 新的支持:foreach

5.4 关于 Map 集合的输出


集合概述

集合:集合是java中提供的一种容器,可以用来存储多个数据。

集合和数组既然都是容器,它们有啥区别呢?

数组的长度是固定的。集合的长度是可变的。 数组中存储的是同一类型的元素,可以存储基本数据类型值。集合存储的都是对象。而且对象的类 型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。

集合框架

集合按照其存储结构可以分为两大类,分别是单列集合 java.util.Collection 和双列集合 java.util.Map,框架图如下所示:

Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是 java.util.List 和java.util.Set 。其中, List 的特点是元素有序、元素可重复。 Set 的特点是元素无序,而且不可重复。 List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList ,Set 接口的主要实现类有 java.util.HashSet 和 java.util.TreeSet。

Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。同时它也没有继承Collection。在Map中它保证了key与value之间的一一对应关系。也就是说一个key对应一个value,所以它不能存在相同的key值,当然value值可以相同。
 

Java集合类[1] | Uzumaki Kyuubi

图片引用于:https://chazyhabit.wordpress.com/2014/07/20/java%E9%9B%86%E5%90%88%E7%B1%BB-1/

  • 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
  • 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
  • 实现类:8个实现类(实线表示),对接口的具体实现。

1. Collection接口

Collection 接口是在整个 Java 类集中保存单值的最大操作父接口,里面每次操作的时候都只能保存一个对象的数据。 此接口定义在 java.util 包中。 此接口定义如下:

public interface Collection<E> extends Iterable<E>

此接口使用了泛型技术,在 JDK 1.5 之后为了使类集操作的更加安全,所以引入了泛型。 此接口的常用方法如下所示。

本接口中以功能更定义了15个方法,那么此接口的全部子类或子接口就将全部继承以上接口中的方法,开发中不会直接使用Collection接口,而是使用其操作的子接口:List、Set。

2. List接口

List接口继承于Collection接口,它可以定义一个允许重复的有序集合。因为List中的元素是有序的,所以我们可以通过使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于Java的数组。

 List 子接口的定义:public interface List<E> extends Collection<E>

此接口上依然使用了泛型技术,有如下的扩充方法:

List 接口拥有比 Collection 接口更多的操作方法。常用的实现类有如下几个:实现List接口的集合主要有:ArrayList(95%)、LinkedList(1%)、Vector、Stack。

2.1 ArrayList

ArrayList 是 List 接口的子类,此类的定义如下:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

此类继承了 AbstractList 类,AbstractList 是 List 接口的子类,用了适配器设计模式。

ArrayList 是一个动态数组结构,支持随机存取,尾部插入删除方便,内部插入删除效率低(因为要移动数组元素);如果内部数组容量不足则自动扩容,因此当数组很大时,效率较低。

范例:增加,打印,移除和遍历元素

import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {
    public static void main(String[] args) {
        List<String> all = new ArrayList<String>(); // 实例化List对象,并指定泛型类型
        all.add("hello "); // 增加内容,此方法从Collection接口继承而来
        all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
        all.add("world"); // 增加内容,此方法从Collection接口继承而来
        System.out.println(all); // 打印all对象调用toString()方法
        all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
        all.remove("world");// 删除指定的对象
        System.out.print("集合中的内容是:");
        for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
            System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
        }
    }
}
  1. 以上的操作向集合中增加了三个元素,其中在指定位置增加的操作是 List 接口单独定义的。
  2. 随后调用 toString()方法进行输出。 可以发现,此时的对象数组并没有长度的限制,长度可以任意长,只要是内存够大就行。
  3. 使用 remove()方法删除若干个元素,并且使用循环的方式输出。
  4. 根据指定位置取的内容的方法,只有 List 接口才有定义,其他的任何接口都没有任何的定义。

2.2 Vector

与 ArrayList 一样,Vector 本身也属于 List 接口的子类,此类的定义如下:

public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable

此类与 ArrayList 类一样,都是 AbstractList 的子类。所以,此时的操作只要是 List 接口的子类就都按照 List 进行操作。

import java.util.List;
import java.util.Vector;

public class VectorDemo {
    public static void main(String[] args) {
        List<String> all = new Vector<String>(); // 实例化List对象,并指定泛型类型
        all.add("hello "); // 增加内容,此方法从Collection接口继承而来
        all.add(0, "LAMP ");// 增加内容,此方法是List接口单独定义的
        all.add("world"); // 增加内容,此方法从Collection接口继承而来
        all.remove(1); // 根据索引删除内容,此方法是List接口单独定义的
        all.remove("world");// 删除指定的对象
        System.out.print("集合中的内容是:");
        for (int x = 0; x < all.size(); x++) { // size()方法从Collection接口继承而来
            System.out.print(all.get(x) + "、"); // 此方法是List接口单独定义的
        }
    }
}

以上的操作结果与使用 ArrayList 本身并没有任何的区别。因为操作的时候是以接口为操作的标准。 但是 Vector 属于 Java 元老级的操作类,是最早的提供了动态对象数组的操作类,在 JDK 1.0 的时候就已经推出了此 类的使用,只是后来在 JDK 1.2 之后引入了 Java 类集合框架。但是为了照顾很多已经习惯于使用 Vector 的用户,所以在 JDK 1.2 之后将 Vector 类进行了升级了,让其多实现了一个 List 接口,这样才将这个类继续保留了下来。

2.2.1 Vector 类和 ArrayList 类的区别

关于Vector,现在用的很少了,因为里面的get、set、add等方法都加了synchronized,所以,执行效率会比较低,如果需要在多线程中使用,可以采用下面语句创建 ArrayList 对象

List<Object> list =Collections.synchronizedList(new ArrayList<Object>());

也可以考虑使用复制容器 java.util.concurrent.CopyOnWriteArrayList进行操作,例如:

final CopyOnWriteArrayList<Object> cowList = new CopyOnWriteArrayList<String>(Object);

2.3 LinkedList

同样实现List接口的LinkedList与ArrayList不同,ArrayList是一个动态数组,而LinkedList是一个双向链表。所以它除了有ArrayList的基本操作方法外还额外提供了get,remove,insert方法在LinkedList的首部或尾部。

在任意位置插入删除都很方便,但是不支持随机取值,每次都只能从一端开始遍历,直到找到查询的对象,然后返回;不过,它不像 ArrayList 那样需要进行内存拷贝,因此相对来说效率较高,但是因为存在额外的前驱和后继节点指针,因此占用的内存比 ArrayList 多一些。

此类的使用几率比较低,定义如下:

public class LinkedList<E> extends AbstractSequentialList<E> implements List<E>, Deque<E>, Cloneable, Serializable

此类继承了AbstractList,所以是List的子类。但是此类也是Queue接口的子类,

Queue 接口定义了如下的方法:

import java.util.LinkedList;
import java.util.Queue;

public class LinkedListDemo {
    public static void main(String[] args) {
        Queue<String> queue = new LinkedList<String>();
        queue.add("A");
        queue.add("B");
        queue.add("C");
        //把queue的大小先取出来,否则每循环一次,移除一个元素,就少一个元素,
        //那么queue.size()在变小,就不能循环queue.size()次了。
        int len=queue.size();
        for (int x = 0; x <len; x++) {
            System.out.println(queue.poll());
        }
        System.out.println(queue);
    }
}

2.3.1 LinkedList 类和 ArrayList 类的比较

  • ArrayList和LinkedList都不是线程安全的,小并发量的情况下可以使用Vector,若并发量很多,且读多写少可以考虑使用CopyOnWriteArrayList。
  • 因为CopyOnWriteArrayList底层使用ReentrantLock锁,比使用synchronized关键字的Vector能更好的处理锁竞争的问题。

2.4 Stack

Stack 是 Vector 的一个子类,本质也是一个动态数组结构,不同的是,它的数据结构是先进后出,取名叫栈!

Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop 方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。

关于Stack,现在用的也很少,因为有个ArrayDeque双端队列,可以替代Stack所有的功能,并且执行效率比它高!

3. Set 接口

Set 接口也是 Collection 的子接口,与 List 接口最大的不同在于,Set 接口里面的内容是不允许重复的,而Set集合中的去重和hashcode与equals方法直接相关。 Set 接口并没有对 Collection 接口进行扩充,基本上还是与 Collection 接口保持一致。因为此接口没有 List 接口中定义 的 get(int index)方法,所以无法使用循环进行输出。 那么在此接口中有两个常用的子类:HashSet、TreeSet

3.1 散列存放:HashSet

既然 Set 接口并没有扩充任何的 Collection 接口中的内容,所以使用的方法全部都是 Collection 接口定义而来的。

HashSet 属于散列的存放类集,里面的内容是无序存放的。

范例:添加元素和遍历

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> all = new HashSet<String>(); // 实例化Set接口对象
        all.add("A");
        all.add("B");
        all.add("C");
        all.add("D");
        all.add("E");
        Object[] obj = all.toArray(); // 将集合变为对象数组
        for (int x = 0; x < obj.length; x++) {
            System.out.print(obj[x] + "、");
        }
    }
}

以上的操作不好,因为在操作的时候已经指定了操作的泛型类型,那么现在最好的做法是由泛型所指定的类 型变为指定的数组。 所以只能使用以下的方法:<T> T[] toArray(T[] a)

String[] str = all.toArray(new String[] {});// 变为指定的泛型类型数组

下面再进一步验证 Set 接口中是不能有重复的内容的。

public static void main(String[] args) {
    Set<String> all = new HashSet<String>(); // 实例化Set接口对象
    all.add("A"); 
    all.add("A"); // 重复元素 
    all.add("A"); // 重复元素 
    all.add("A"); // 重复元素 
    all.add("A"); // 重复元素 
    all.add("B"); 
    all.add("C"); 
    all.add("D"); 
    all.add("E"); 
    System.out.println(all);
}

Set 接口中是不能有任何的重复元素的,所以其最终结果只能有一个“A”。

HashSet使用和理解中容易出现的误区:

  1. HashSet中是允许存入null值的,但是在HashSet中仅仅能够存入一个null值。
  2. HashSet中存储的元素的是无序的,但是由于HashSet底层是基于Hash算法实现的,使用了hashcode,所以HashSet中相应的元素的位置是固定的。
  3. 必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

3.2 LinkedHashSet

LinkedHashSet继承自HashSet,其底层是基于LinkedHashMap来实现的,有序,非同步。LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

3.3 排序的子类:TreeSet

与 HashSet 不同的是, TreeSet 本身属于排序的子类,此类的定义如下:

public class TreeSet<E> extends AbstractSet<E> implements NavigableSet<E>, Cloneable, Serializable

下面通过代码来观察其是如何进行排序的。

import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo {
    public static void main(String[] args) {
        Set<String> all = new TreeSet<String>(); // 实例化Set接口对象
        all.add("D");
        all.add("X");
        all.add("A");
        System.out.println(all);
    }
}

输出:[A, D, X]

3.3.1 排序的说明

既然 Set 接口的 TreeSet 类本身是允许排序,那么现在自定义一个类是否可以进行对象的排序呢?

public class Person {
    private String name;
    private int age;

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

下面定义一个 TreeSet 集合,向里面增加若干个 Person 对象。

执行以上的操作代码之后,发现出现了如下的错误提示:

此时的提示是:Person 类不能向 Comparable 接口转型的问题? 所以,证明如果现在要是想进行排序的话,则必须在 Person 类中实现 Comparable 接口。

public class Person implements Comparable<Person>{
    private String name;
    private int age;

    public int compareTo(Person per) {
        if (this.age > per.age) {
            return 1;
        } else if (this.age < per.age) {
            return -1;
        } else {
            return 0;
        }
    }

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String toString() {
        return "姓名:" + this.name + ",年龄:" + this.age;
    }
}

修改Person代码之后结果为:

[姓名:张三,年龄:10, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:孙七,年龄:13]

从以上的结果中可以发现,李四没有了。因为李四的年龄和张三的年龄是一样的,所以会被认为是同一个对象。则此时必须修改 Person 类,如果假设年龄相等的话,按字符串进行排序。

    public int compareTo(Person per) {
        if (this.age > per.age) {
            return 1;
        } else if (this.age < per.age) {
            return -1;
        } else {
            return this.name.compareTo(per.name);
        }
    }

结果:[姓名:张三,年龄:10, 姓名:李四,年龄:10, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:孙七,年龄:13]

3.3.2 关于重复元素的说明

之前使用 Comparable 完成的对于重复元素的判断,那么 Set 接口定义的时候本身就是不允许重复元素的,那么证明如果现在真的是有重复元素的话,使用 HashSet 也同样可以进行区分。

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo1 {
    public static void main(String[] args) {
        Set<Person> all = new HashSet<Person>();
        all.add(new Person("张三", 10));
        all.add(new Person("李四", 10));
        all.add(new Person("李四", 10));
        all.add(new Person("王五", 11));
        all.add(new Person("赵六", 12));
        all.add(new Person("孙七", 13));
        System.out.println(all);
    }
}

结果:[姓名:孙七,年龄:13, 姓名:李四,年龄:10, 姓名:赵六,年龄:12, 姓名:李四,年龄:10, 姓名:张三,年龄:10, 姓名:王五,年龄:11]

发现并没有去掉所谓的重复元素,也就是说之前的操作并不是真正的重复元素的判断,而是通过 Comparable 接口间接完成的。

如果要想判断两个对象是否相等,则必须使用 Object 类中的 equals()方法。 从最正规的来讲,如果要想判断两个对象是否相等,则有两种方法可以完成:

  1. 第一种判断两个对象的编码是否一致,这个方法需要通过 hashCode()完成,即:每个对象有唯一的编码
  2. 还需要进一步验证对象中的每个属性是否相等,需要通过 equals()完成。 所以此时需要覆写 Object 类中的 hashCode()方法,此方法表示一个唯一的编码,一般是通过公式计算出来的。

在Person类中可以generate自动产生equals和hashcode的方法重写即可,如下所示:

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return age == person.age &&
                Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }

结果为:[姓名:李四,年龄:10, 姓名:孙七,年龄:13, 姓名:王五,年龄:11, 姓名:赵六,年龄:12, 姓名:张三,年龄:10]

小结:

  • 关于 TreeSet 的排序实现,如果是集合中对象是自定义的或者说其他系统定义的类没有实现 Comparable 接口,则不能实现 TreeSet 的排序,会报类型转换(转向 Comparable 接口)错误。 换句话说要添加到 TreeSet 集合中的对象的类型必须实现了 Comparable 接口。
  • 不过 TreeSet 的集合因为借用了 Comparable 接口,同时可以去除重复值,而 HashSet 虽然是 Set 接口子类,但是对于没有复写 Object 的 equals 和 hashCode 方法的对象,加入了 HashSet 集合中也是不能去掉重复值的。

4. Map 接口

以上的 Collection 中,每次操作的都是一个对象,如果现在假设要操作一对对象,则就必须使用 Map 了,Map与List、Set接口不同,它是由一系列键值对组成的集合,提供了key到Value的映射。Map中它保证了key与value之间的一一对应关系。

接口定义如:public interface Map<K,V>

此接口与 Collection 接口没有任何的关系,是第二大的集合操作接口。此接口常用方法如下:

Map 本身是一个接口,所以一般会使用以下的几个子类:HashMap、TreeMap、Hashtable

4.1 新的子类:HashMap

HashMap 是 Map 的子类,此类的定义如下:

public class HashMap<K,V> extends AbstractMap<K,V> implements Map<K,V>, Cloneable, Serializable

此类继承了 AbstractMap 类,同时可以被克隆,可以被序列化下来。

范例:Map中增加键值对,获得key,value值和遍历

import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        Map<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "张三A"); // 添加Integer-String键值对
        map.put(2, "李四");
        map.put(3, "王五");
        Set<Integer> set = map.keySet(); // 得到全部的key
        Collection<String> value = map.values(); // 得到全部的value
        Iterator<Integer> iter1 = set.iterator();
        Iterator<String> iter2 = value.iterator();
        System.out.print("全部的key:");

        while (iter1.hasNext()) {
            System.out.print(iter1.next() + "、");
        }

        System.out.print("\n全部的value:");

        while (iter2.hasNext()) {
            System.out.print(iter2.next() + "、");
        }
    }
}

结果:全部的key:1、2、3、
全部的value:张三A、李四、王五、

也可以通过Iterator来遍历,后续会讲解遍历的方式

4.2 旧的子类:Hashtable

Hashtable 是一个最早的 key-value 的操作类,本身是在 JDK 1.0 的时候推出的。其基本操作与 HashMap 是类似的。

import java.util.*;

public class HashTableDemo {
    public static void main(String[] args) {
        Map<String, Integer> numbers = new Hashtable<String, Integer>();
        numbers.put("one", 1);
        numbers.put("two", 2);
        numbers.put("three", 3);
        Integer n = numbers.get("two");
        if (n != null) {
            System.out.println("two = " + n);
        }
    }
}

操作的时候,可以发现与 HashMap 基本上没有什么区别,而且本身都是以 Map 为操作标准的,所以操作的结果形式 都一样。但是 Hashtable 中是不能向集合中插入 null 值的。

4.2.1 HashMap 与 Hashtable 的区别

在整个集合中除了 ArrayList 和 Vector 的区别之外,另外一个最重要的区别就是 HashMap 与 Hashtable 的区别。

4.3 排序的子类:TreeMap

TreeMap 子类是允许 key 进行排序的操作子类,其本身在操作的时候将按照 key 进行排序,另外,key 中的内容可以 为任意的对象,但是要求对象所在的类必须实现 Comparable 接口。

import java.util.*;

public class TreeMapDemo {
    public static void main(String[] args) {
        Map<String, String> map = new TreeMap<String, String>();
        map.put("ZS", "张三");
        map.put("LS", "李四");
        map.put("WW", "王五");
        map.put("ZL", "赵六");
        map.put("SQ", "孙七");

        Set<String> set = map.keySet(); // 得到全部的key
        Iterator<String> iter = set.iterator();
        while (iter.hasNext()) {
            String i = iter.next(); // 得到key
            System.out.println(i + " --:> " + map.get(i));
        }
    }
}

结果:

LS --:> 李四
SQ --:> 孙七
WW --:> 王五
ZL --:> 赵六
ZS --:> 张三

此时的结果已经排序成功了,但是从一般的开发角度来看,在使用 Map 接口的时候并不关心其是否排序,所以此类 只需要知道其特点即可。

5. 集合输出

已经学习过了基本的集合操作,那么对于集合的输出本身也是有多种形式的,可以使用如下的几种方式:

Iterator 迭代输出(90%)、ListIterator(5%)、Enumeration(1%)、foreach(4%)

输出的时候一定要记住以下的原则:“只要是碰到了集合,则输出的时候想都不想就使用 Iterator”(需要了解)

5.1 Iterator

Iterator 属于迭代输出,基本的操作原理:是不断的判断是否有下一个元素,有的话,则直接输出。 此接口定义如下:

public interface Iterator<E>

要想使用此接口,则必须使用 Collection 接口,在 Collection 接口中规定了一个 iterator()方法,可以用于为 Iterator 接口进行实例化操作。 此接口规定了以下的三个方法:

通过 Collection 接口为其进行实例化之后,一定要记住,Iterator 中的操作指针是在第一条元素之上,当调用 next()方 法的时候,获取当前指针指向的值并向下移动,使用 hasNext()可以检查序列中是否还有元素。

 范例:观察 Iterator 输出

    public static void main(String[] args) {
        Collection<String> all = new ArrayList<String>();
        all.add("A");
        all.add("B");
        all.add("C");
        all.add("D");
        all.add("E");

        Iterator<String> iter = all.iterator();
        while (iter.hasNext()) {// 判断是否有下一个元素
            String str = iter.next(); // 取出当前元素
            System.out.print(str + "、");
        }
    }

以上的操作是 Iterator 接口使用最多的形式,也是一个标准的输出形式。 但是在使用 Iterator 输出的时候有一点必须注意,在进行迭代输出的时候如果要想删除当前元素,则只能使用 Iterator 接口中的 remove()方法, 而不能使用集合中的 remove()方法。 否则将出现未知的错误。

从实际的开发角度看,元素的删除操作出现的几率是很小的,基本上可以忽略,即:集合中很少有删除元素 的操作。

5.2 ListIterator

ListIterator 是可以进行双向输出的迭代接口,此接口定义如下:

public interface ListIterator<E> extends Iterator<E>

此接口是 Iterator 的子接口,此接口中定义了以下的操作方法:

5.3 新的支持:foreach

foreach 可以用来输出数组的内容,那么也可以输出集合中的内容。

    public static void main(String[] args) {
        Collection<String> all = new ArrayList<String>();
        all.add("A");
        all.add("B");
        all.add("C");
        all.add("D");
        all.add("E");
        for (String str : all) {
            System.out.println(str);
        }
    }

在使用 foreach 输出的时候一定要注意的是,里面的操作泛型要指定具体的类型,这样在输出的时候才会更加有针对性。

5.4 关于 Map 集合的输出

在 Collection 接口中,可以使用 iterator()方法为 Iterator 接口实例化,并进行输出操作,但是在Map接口中并没有此方法的定义,所以 Map 接口本身是不能直接使用 Iterator 进行输出的。

因为 Map 接口中存放的每一个内容都是一对值,而使用 Iterator 接口输出的时候,每次取出的都实际上是一个完整的 对象。如果此时非要使用 Iterator 进行输出的话,则可以按照如下的步骤进行:

  1. 使用 Map 接口中的 entrySet()方法将 Map 接口的全部内容变为 Set 集合
  2. 可以使用 Set 接口中定义的 iterator()方法为 Iterator 接口进行实例化
  3. 之后使用 Iterator 接口进行迭代输出,每一次的迭代都可以取得一个 Map.Entry 的实例
  4. 通过 Map.Entry 进行 key 和 value 的分离

那么,到底什么是 Map.Entry 呢?

Map.Entry 本身是一个接口。此接口是定义在 Map 接口内部的,是 Map 的内部接口。此内部接口使用 static 进行定义, 所以此接口将成为外部接口。 实际上来讲,对于每一个存放到 Map 集合中的 key 和 value 都是将其变为了 Map.Entry 并且将 Map.Entry 保存在了 Map 集合之中。

在 Map.Entry 接口中以下的方法最为常用:

范例:使用 Iterator 输出 Map 接口

    public static void main(String[] args) {
        Map<String, String> map = new HashMap<String, String>();
        map.put("ZS", "张三");
        map.put("LS", "李四");
        map.put("WW", "王五");
        map.put("ZL", "赵六");
        map.put("SQ", "孙七");

        Set<Map.Entry<String, String>> set = map.entrySet();// 变为Set实例
        Iterator<Map.Entry<String, String>> iter = set.iterator();
        while (iter.hasNext()) {
            Map.Entry<String, String> me = iter.next();
            System.out.println(me.getKey() + " --> " + me.getValue());
        }
    }

结果:

WW --> 王五
ZL --> 赵六
LS --> 李四
ZS --> 张三
SQ --> 孙七

以上的代码一定要记住,Map集合中每一个元素都是Map.Entry的实例,只有通过Map.Entry才能进行key和value的分离操作。 除了以上的做法之外,在 JDK 1.5 之后也可以使用 foreach 完成同样的输出,只是这样的操作基本上不使用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值