Java 集合框架综述

1.集合的由来

    Java是面向对象编程语言,经常操作很多对象,必要时需存储对象(达到复用或管理等目的),常见容器如数组和StringBuffer(或StringBuilder,前者线程安全,效率较低,为了提高效率而引进)。

    通常数组的长度固定,所以不适合做变化的需求(可以手动去重新分配调整,较麻烦),而StringBuffer(或StringBuilder)存储元素为字符串,皆不符合存储任意元素的要求。因此集合就应运而生了。

 

2.集合和数组的区别?

(1) 长度限制之别

    数组长度是固定不变的

    集合的大小是可动态变化的

(2) 存储类型之别

    一个数组存储的元素可以是基本类型,也可以是引用类型,且只能存储同一种类型的元素

    一个集合存储的元素只能是引用类型,但集合可以存储不同类型的元素(但集合一般存储同一种类型,可以用泛型加以控制)

(3) 访问元素方式

    数组是根据索引来获取元素的

    集合通常会提供一个迭代器来方便访问元素

 

3. 集合框架图:

    所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。

 

4.总体分析

大致说明:

看上面的框架图,先抓住它的主干,即Collection和Map。

(1) Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection包含了List和Set两大分支。

    ① List是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是0。List的实现类有LinkedList, ArrayList, Vector, Stack。

    ② Set是一个不允许有重复元素的集合。Set的实现类有HastSet和TreeSet。HashSet依赖于HashMap,它实际上是通过HashMap实现的;TreeSet依赖于TreeMap,它实际上是通过TreeMap实现的。

(2) Map是一个映射接口,即key-value键值对。Map中的每一个元素包含“一个key”和“key对应的value”。AbstractMap是个抽象类,它实现了Map接口中的大部分API。而HashMap,TreeMap,WeakHashMap都是继承于AbstractMap。Hashtable虽然继承于Dictionary,但它实现了Map接口。

(3) 接下来,再看Iterator。它是遍历集合的工具,即我们通常通过Iterator迭代器来遍历集合。我们说Collection依赖于Iterator,是因为Collection的实现类都要实现iterator()函数,返回一个Iterator对象。ListIterator是专门为遍历List而存在的。

(4) 再看Enumeration,它是JDK 1.0引入的抽象类。作用和Iterator一样,也是遍历集合;但是Enumeration的功能要比Iterator少。在上面的框图中,Enumeration只能在Hashtable, Vector, Stack中使用。

(5) 最后,看Arrays和Collections。它们是操作数组、集合的两个工具类。

    有了上面的整体框架之后,我们接下来对每个类分别进行分析。

 

5.Collection 接口

    Collection接口是处理对象集合的根接口,其中定义了很多对元素进行操作的方法。Collection接口有两个主要的子接口List和Set,注意Map不是Collection的子接口,这个要牢记

Collection接口中的方法如下:

    另外,Collection中有一个iterator()函数,它的作用是返回一个Iterator接口。通常,我们通过Iterator迭代器来遍历集合。ListIterator是List接口所特有的,在List接口中,通过ListIterator()返回一个ListIterator对象。    其中,有几个比较常用的方法,比如方法add()添加一个元素到集合中,addAll()将指定集合中的所有元素添加到集合中,contains()方法检测集合中是否包含指定的元素,toArray()方法返回一个表示集合的数组。

Collection接口有两个常用的子接口,下面详细介绍。

5.1  List 接口: 有序的,允许重复的, 因为List体系集合都有索引值。

(1) ArrayList : 采用数组结构存储元素,查询操作多时选择,是 List 接口的典型实现类。本质上,ArrayList对象引用的一个变长数组,是线程不安全的。而 Vector 是线程安全的,即使为保证 List 集合线程安全,也不推荐使用Vector 。

    ArrayList是一个动态数组,也是我们最常用的集合。它允许任何符合规则的元素插入甚至包括null。每一个ArrayList都有一个初始容量(10),该容量代表了数组的大小。随着容器中的元素不断增加,容器的大小也会随着增加。在每次向容器中增加元素的同时都会进行容量检查,当快溢出时,就会进行扩容操作。所以如果我们明确所插入元素的多少,最好指定一个初始容量值,避免过多的进行扩容操作而浪费时间、效率。

    size、isEmpty、get、set、iterator 和 listIterator 操作都以固定时间运行。add 操作以分摊的固定时间运行,也就是说,添加 n 个元素需要 O(n) 时间(由于要考虑到扩容,所以这不只是添加元素会带来分摊固定时间开销那样简单)。

    ArrayList擅长于随机访问。同时ArrayList是非同步的。

void add(int index, Object ele)

boolean addAll(int index, Collection eles)

Object get(int index)

int indexOf(Object obj)

int lastIndexOf(Object obj)

Object remove(int index)

Object set(int index, Object ele)

List subList(int fromIndex, int toIndex)

(2) LinkedList : 采用链表结构存储元素,增删操作多时选择。

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

    由于实现的方式不同,LinkedList不能随机访问,它所有的操作都是要按照双重链表的需要执行。在列表中索引的操作将从开头或结尾遍历列表(从靠近指定索引的一端)。这样做的好处就是可以通过较低的代价在List中进行插入和删除操作。

    与ArrayList一样,LinkedList也是非同步的。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

List list = Collections.synchronizedList(new LinkedList(...));

void addFirst(Object obj)

void addLast(Object obj)   

Object getFirst()

Object getLast()

Object removeFirst()

Object removeLast()

(3) Vector :Vector 是一个古老的集合,JDK1.0就有了,大多数操作与ArrayList相同,区别之处在于Vector是同步的,所以说Vector是线程安全的动态数组。

void addElement(Object obj)

void insertElementAt(Object obj,int index)

void setElementAt(Object obj,int index)

void removeElement(Object obj)

void removeAllElements()

(4)Stack

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

5.2  List 集合的遍历

(1) 使用 增强for 循环

ArrayList al  = new ArrayList();
al.add("AA");
al.add("BB");
for(Object obj : al){
     System.out.println(obj);
}

(2) 使用 Iterator 迭代器

ArrayList al  = new ArrayList();
al.add("AA");
al.add("BB");
Iterator it = al.iterator();
while(it.hasNext()){
     Object obj = it.next();
     System.out.println(obj);
}

(3) ListIterator 列表迭代器:是List的特有迭代器

List<String> strList = new ArrayList<String>();
strList.add("string1");
strList.add("string2");
strList.add("string3");
strList.add("string4");
strList.add("string5");
strList.add("string6");
Iterator<String> it = strList.iterator();
while (it.hasNext()) {
    String s = it.next();
    if ("string2".equals(s)) {
        it.remove();
    }
}

(4) 普通for循环 增强for循环 迭代器循环有什么区别?

     ① 增强for循环和iterator遍历的效果是一样的,也就说增强for循环的内部也就是调用iteratoer实现的(可以查看编译后的文件),但是增强for循环有些缺点,例如不能在增强循环里动态的删除集合内容。不能获取下标等。

     ② ArrayList由于使用数组实现,因此下标明确,最好使用普通循环。

     ③ 而对于 LinkedList 由于获取一个元素,要从头开始向后找,因此建议使用 增强for循环,也就是iterator。

     ④ 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

5.3 Set 接口:无序的,不允许重复的。

    Set是一种不包括重复元素的Collection。它维持它自己的内部排序,所以随机访问没有任何意义。与List一样,它同样允许null的存在但是仅有一个。由于Set接口的特殊性,所有传入Set集合中的元素都必须不同,同时要注意任何可变对象,如果在对集合中元素进行操作时,导致e1.equals(e2)==true,则必定会产生某些问题。Set接口有三个具体实现类,分别是散列集HashSet、链式散列集LinkedHashSet和树形集TreeSet。

     Set是一种不包含重复的元素的Collection,无序,即任意的两个元素e1和e2都有e1.equals(e2)=false,Set最多有一个null元素。需要注意的是:虽然Set中元素没有顺序,但是元素在set中的位置是由该元素的HashCode决定的,其具体位置其实是固定的。

(1) HashSet:是 Set 接口的典型实现类,大多数时候使用 Set 集合时都使用这个实现类,HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。 

     HashSet 具有以下特点:

     ① 不能保证元素的排列顺序

     ② HashSet 不是线程安全的

     ③ 集合元素可以是 null

     ④ 判断元素是否存在的依据是,先比较 hashCode ,若hashCode不存在,则直接存储。若 hashCode 存在,再通过 equals() 比较两个对象的内容。

(2) LinkedHashSet: 是 HashSet 的子类,相较于HashSet 多了链表维护元素之间的顺序,遍历操作效率高,增删操作效率低。

     LinkedHashSet 具有以下特点:

    ① LinkedHashSet 是 HashSet 的子类

    ② LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

    ③ LinkedHashSet 插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能。

    ④ LinkedHashSet 不允许集合元素重复。

(3) TreeSet : TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

    TreeSet是一个有序集合,其底层是基于TreeMap实现的,非线程安全。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。当我们构造TreeSet时,若使用不带参数的构造函数,则TreeSet的使用自然比较器;若用户需要使用自定义的比较器,则需要使用带比较器的参数。

    注意:TreeSet集合不是通过hashcode和equals函数来比较元素的.它是通过compare或者comparaeTo函数来判断元素是否相等.compare函数通过判断两个对象的id,相同的id判断为重复元素,不会被加入到集合中。

5.4 Set 集合的遍历

(1) for循环遍历: 

for (String str : set) {
      System.out.println(str);
}

(2) 迭代遍历: 

Set<String> set = new HashSet<String>();
Iterator<String> it = set.iterator();
while (it.hasNext()) {
  String str = it.next();
  System.out.println(str);
}

6.Map接口

6.1 Map接口

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

(1) HashMap : 是 Map 接口的典型实现类

    以哈希表数据结构实现,查找对象时通过哈希函数计算其位置,它是为快速查询而设计的,其内部定义了一个hash表数组(Entry[] table),元素会通过哈希转换函数将元素的哈希地址转换成数组中存放的索引,如果有冲突,则使用散列链表的形式将所有相同哈希地址的元素串起来,可能通过查看HashMap.Entry的源码它是一个单链表结构。

    HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。

    HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。

(2) LinkedHashMap:是 HashMap的子类,相较于 HashMap 多了链表维护元素的顺序

    LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。

    LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

    LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。

根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表。默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。

    注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。

    由于LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能,但在迭代访问Map里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。

(3) TreeMap : 根据key拥有自己的排序方式

    TreeMap 是一个有序的key-value集合,非同步,基于红黑树(Red-Black tree)实现,每一个key-value节点作为红黑树的一个节点。TreeMap存储时会进行排序的,会根据key来对key-value键值对进行排序,其中排序方式也是分为两种,一种是自然排序,一种是定制排序,具体取决于使用的构造方法。

    自然排序:TreeMap中所有的key必须实现Comparable接口,并且所有的key都应该是同一个类的对象,否则会报ClassCastException异常。

    定制排序:定义TreeMap时,创建一个comparator对象,该对象对所有的treeMap中所有的key值进行排序,采用定制排序的时候不需要TreeMap中所有的key必须实现Comparable接口。

    TreeMap判断两个元素相等的标准:两个key通过compareTo()方法返回0,则认为这两个key相等。

    如果使用自定义的类来作为TreeMap中的key值,且想让TreeMap能够良好的工作,则必须重写自定义类中的equals()方法,TreeMap中判断相等的标准是:两个key通过equals()方法返回为true,并且通过compareTo()方法比较应该返回为0。

(4) Hashtable : 是一个古老的实现,是线程安全的,因此相较于 HashMap 效率低。

(5) Properties :Properties 类是 Hashtable 的子类,该对象用于处理属性文件。

6.2 Map的常用方法

Object get(Object key)

boolean containsKey(Object key)

boolean containsValue(Object value)

int size()

boolean isEmpty()

boolean equals(Object obj)

Object put(Object key,Object value)

Object remove(Object key)

void putAll(Map t)

void clear()

6.3 Map集合的遍历

//遍历 Map 的方式一: 获取 Map 中所有的 key    keySet()
@Test
public static void main(String[] args) {
         Map<String, Integer> map = new HashMap<String, Integer>();
         map.put("张三", 99);
         map.put("李四", 88);
         map.put("王五", 77);
         Set<String> set = map.keySet();
         System.out.println(set);
}
//遍历 Map 的方式二: 获取 Map 中所有的 value    values()
public static void main(String[] args) {
       Map<String, Integer> map = new HashMap<String, Integer>();
         map.put("张三", 99);
         map.put("李四", 88);
         map.put("王五", 77);
         Collection<Integer> coll = map.values();
         System.out.println(coll);
}
//遍历 Map 的方式三: 获取 Map 中所有的 Entry(Entry是 Map的内部类 一个 Entry 对应着一个 key和一个  value)   entrySet()
public static void main(String[] args) {
    Map<String, Integer> map = new HashMap<String, Integer>();
     map.put("张三", 99);
     map.put("李四", 88);
     map.put("王五", 77);
     Set<Entry<String, Integer>> set = map.entrySet();
     Iterator<Entry<String, Integer>> it = set.iterator();
     while(it.hasNext()){
          Entry<String, Integer> entry = it.next();
          String key = entry.getKey();
          Integer value = entry.getValue();
          System.out.println(key);
          System.out.println(value);
     }
}

7.Iterator 与 ListIterator详解

(1) Iterator:Iterator是一个接口,它是集合的迭代器。集合可以通过Iterator去遍历集合中的元素。Iterator提供的API接口如下:

    boolean hasNext():判断集合里是否存在下一个元素。如果有,hasNext()方法返回 true。

    Object next():返回集合里下一个元素。

    void remove():删除集合里上一次next方法返回的元素。

使用示例:

public class IteratorExample {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        Iterator<String> it = a.iterator();
        while (it.hasNext()) {
            String t = it.next();
            if ("bbb".equals(t)) {
                it.remove();
            }
        }
        System.out.println("After iterate : " + a);
    }
}

//输出结果如下:
Before iterate : [aaa, bbb, ccc]
After iterate : [aaa, ccc]

注意:

    ① Iterator只能单向移动。

    ② Iterator.remove()是唯一安全的方式来在迭代过程中修改集合;如果在迭代过程中以任何其它的方式修改了基本集合将会产生未知的行为。而且每调用一次next()方法,remove()方法只能被调用一次,如果违反这个规则将抛出一个异常。

(2) ListIterator

    ListIterator是一个功能更加强大的迭代器, 它继承于Iterator接口,只能用于各种List类型的访问。可以通过调用listIterator()方法产生一个指向List开始处的ListIterator, 还可以调用listIterator(n)方法创建一个一开始就指向列表索引为n的元素处的ListIterator.

ListIterator接口定义如下:

public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
    E next();
    boolean hasPrevious();
    E previous();
    int nextIndex();
    int previousIndex();
    void remove();
    void set(E e);
    void add(E e);
}

由以上定义我们可以推出ListIterator可以:
(1)双向移动(向前/向后遍历).
(2)产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引.
(3)可以使用set()方法替换它访问过的最后一个元素.
(4)可以使用add()方法在next()方法返回的元素之前或previous()方法返回的元素之后插入一个元素.

使用示例

public class ListIteratorExample {
    public static void main(String[] args) {
        ArrayList<String> a = new ArrayList<String>();
        a.add("aaa");
        a.add("bbb");
        a.add("ccc");
        System.out.println("Before iterate : " + a);
        ListIterator<String> it = a.listIterator();
        while (it.hasNext()) {
            System.out.println(it.next() + ", " + it.previousIndex() + ", " + it.nextIndex());
        }
        while (it.hasPrevious()) {
            System.out.print(it.previous() + " ");
        }
        System.out.println();
        it = a.listIterator(1);
        while (it.hasNext()) {
            String t = it.next();
            System.out.println(t);
            if ("ccc".equals(t)) {
                it.set("nnn");
            } else {
                it.add("kkk");
            }
        }
        System.out.println("After iterate : " + a);
    }
}

//输出结果如下:
Before iterate : [aaa, bbb, ccc]
aaa, 0, 1
bbb, 1, 2
ccc, 2, 3
ccc bbb aaa
bbb
ccc
After iterate : [aaa, bbb, kkk, nnn]

8.ArrayList和LinkedList

    (1) ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。

    (2) 对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。

    (3) 对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。

    这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

 

9.HashTable与HashMap

相同点:

(1)都实现了Map、Cloneable、java.io.Serializable接口。

(2)都是存储"键值对(key-value)"的散列表,而且都是采用拉链法实现的。

不同点:

(1)历史原因:HashTable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现 。

(2)同步性:HashTable是线程安全的,也就是说是同步的,而HashMap是线程序不安全的,不是同步的 。

(3)对null值的处理:HashMap的key、value都可为null,HashTable的key、value都不可为null 。

(4)基类不同:HashMap继承于AbstractMap,而Hashtable继承于Dictionary。

        Dictionary是一个抽象类,它直接继承于Object类,没有实现任何接口。Dictionary类是JDK 1.0的引入的。虽然Dictionary也支持“添加key-value键值对”、“获取value”、“获取大小”等基本操作,但它的API函数比Map少;而且Dictionary一般是通过Enumeration(枚举类)去遍历,Map则是通过Iterator(迭代M器)去遍历。 然而由于Hashtable也实现了Map接口,所以,它即支持Enumeration遍历,也支持Iterator遍历。

      AbstractMap是一个抽象类,它实现了Map接口的绝大部分API函数;为Map的具体实现类提供了极大的便利。它是JDK 1.2新增的类。

(5)支持的遍历种类不同:HashMap只支持Iterator(迭代器)遍历。而Hashtable支持Iterator(迭代器)和Enumeration(枚举器)两种方式遍历。

 

10.HashMap、Hashtable、LinkedHashMap和TreeMap比较

    Hashmap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。遍历时,取得数据的顺序是完全随机的。HashMap最多只允许一条记录的键为Null;允许多条记录的值为Null;HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用Collections的synchronizedMap方法使HashMap具有同步的能力。

    Hashtable 与 HashMap类似,不同的是:它不允许记录的键或者值为空;它支持线程的同步,即任一时刻只有一个线程能写Hashtable,因此也导致了Hashtale在写入时会比较慢。

    LinkedHashMap保存了记录的插入顺序,在用Iterator遍历LinkedHashMap时,先得到的记录肯定是先插入的,也可以在构造时用带参数,按照应用次数排序。在遍历的时候会比HashMap慢,不过有种情况例外,当HashMap容量很大,实际数据较少时,遍历起来可能会比LinkedHashMap慢,因为LinkedHashMap的遍历速度只和实际数据有关,和容量无关,而HashMap的遍历速度和他的容量有关。如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。                    LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序。对于    LinkedHashMap而言,它继承与HashMap、底层使用哈希表与双向链表来保存所有元素。其基本操作与父类HashMap相似,它通过重写父类相关的方法,来实现自己的链接列表特性。

    TreeMap实现SortMap接口,内部实现是红黑树。能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator 遍历TreeMap时,得到的记录是排过序的。TreeMap不允许key的值为null。非同步的。

    一般情况下,我们用的最多的是HashMap,HashMap里面存入的键值对在取出的时候是随机的,它根据键的HashCode值存储数据,根据键可以直接获取它的值,具有很快的访问速度。在Map 中插入、删除和定位元素,HashMap 是最好的选择。

    TreeMap取出来的是排序后的键值对。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。

    LinkedHashMap 是HashMap的一个子类,如果需要输出的顺序和输入的相同,那么用LinkedHashMap可以实现,它还可以按读取顺序来排列,像连接池中可以应用。

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.TreeMap;

public class MapTest {

    public static void main(String[] args) {

        //HashMap
        HashMap<String,String> hashMap = new HashMap();
        hashMap.put("4", "d");
        hashMap.put("3", "c");
        hashMap.put("2", "b");
        hashMap.put("1", "a");

        Iterator<String> iteratorHashMap = hashMap.keySet().iterator();

        System.out.println("HashMap-->");

        while (iteratorHashMap.hasNext()){

            Object key1 = iteratorHashMap.next();
            System.out.println(key1 + "--" + hashMap.get(key1));
        }

        //LinkedHashMap
        LinkedHashMap<String,String> linkedHashMap = new LinkedHashMap();
        linkedHashMap.put("4", "d");
        linkedHashMap.put("3", "c");
        linkedHashMap.put("2", "b");
        linkedHashMap.put("1", "a");

        Iterator<String> iteratorLinkedHashMap = linkedHashMap.keySet().iterator();

        System.out.println("LinkedHashMap-->");

        while (iteratorLinkedHashMap.hasNext()){

            Object key2 = iteratorLinkedHashMap.next();
            System.out.println(key2 + "--" + linkedHashMap.get(key2));
        }

        //TreeMap
        TreeMap<String,String> treeMap = new TreeMap();
        treeMap.put("4", "d");
        treeMap.put("3", "c");
        treeMap.put("2", "b");
        treeMap.put("1", "a");

        Iterator<String> iteratorTreeMap = treeMap.keySet().iterator();

        System.out.println("TreeMap-->");

        while (iteratorTreeMap.hasNext()){

            Object key3 = iteratorTreeMap.next();
            System.out.println(key3 + "--" + treeMap.get(key3));
        }

    }

}

//输出结果:
HashMap-->
3--c
2--b
1--a
4--d
LinkedHashMap-->
4--d
3--c
2--b
1--a
TreeMap-->
1--a
2--b
3--c
4--d

10.HashSet、LinkedHashSet、TreeSet比较

(1) Set接口

    Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。

    Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。

(2) HashSet

HashSet有以下特点:

    ->  不能保证元素的排列顺序,顺序有可能发生变化。

    ->  不是同步的。

    ->  集合元素可以是null,但只能放入一个null。

    当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。

    注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

(3) LinkedHashSet

    LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。

    LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

(4) TreeSet类

    TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。

    TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0。

自然排序

    自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。

    Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是负数,则表明obj1小于obj2。如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0。

定制排序

    自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。

/**  
* @description 几个set的比较  
*    HashSet:哈希表是通过使用称为散列法的机制来存储信息的,元素并没有以某种特定顺序来存放;  
*    LinkedHashSet:以元素插入的顺序来维护集合的链接表,允许以插入的顺序在集合中迭代;  
*    TreeSet:提供一个使用树结构存储Set接口的实现,对象以升序顺序存储,访问和遍历的时间很快。  
* @author Zhou-Jingxian  
*  
*/  
public class SetDemo {  
  
    public static void main(String[] args) {  
  
        HashSet<String> hs = new HashSet<String>();  
        hs.add("B");  
        hs.add("A");  
        hs.add("D");  
        hs.add("E");  
        hs.add("C");  
        hs.add("F");  
        System.out.println("HashSet 顺序:\n"+hs);  
          
        LinkedHashSet<String> lhs = new LinkedHashSet<String>();  
        lhs.add("B");  
        lhs.add("A");  
        lhs.add("D");  
        lhs.add("E");  
        lhs.add("C");  
        lhs.add("F");  
        System.out.println("LinkedHashSet 顺序:\n"+lhs);  
          
        TreeSet<String> ts = new TreeSet<String>();  
        ts.add("B");  
        ts.add("A");  
        ts.add("D");  
        ts.add("E");  
        ts.add("C");  
        ts.add("F");  
        System.out.println("TreeSet 顺序:\n"+ts);  
    }  
}

//输出结果:
HashSet 顺序:[D, E, F, A, B, C]
LinkedHashSet 顺序:[B, A, D, E, C, F]
TreeSet 顺序:[A, B, C, D, E, F]

11.Iterator和ListIterator区别

     我们在使用List,Set的时候,为了实现对其数据的遍历,我们经常使用到了Iterator(迭代器)。使用迭代器,你不需要干涉其遍历的过程,只需要每次取出一个你想要的数据进行处理就可以了。但是在使用的时候也是有不同的。List和Set都有iterator()来取得其迭代器。对List来说,你也可以通过listIterator()取得其迭代器,两种迭代器在有些时候是不能通用的,Iterator和ListIterator主要区别在以下方面:

(1)ListIterator有add()方法,可以向List中添加对象,而Iterator不能

(2)ListIterator和Iterator都有hasNext()和next()方法,可以实现顺序向后遍历,但是ListIterator有hasPrevious()和previous()方法,可以实现逆向(顺序向前)遍历。Iterator就不可以。

(3)ListIterator可以定位当前的索引位置,nextIndex()和previousIndex()可以实现。Iterator没有此功能。

(4)都可实现删除对象,但是ListIterator可以实现对象的修改,set()方法可以实现。Iierator仅能遍历,不能修改。

    因为ListIterator的这些功能,可以实现对LinkedList等List数据结构的操作。其实,数组对象也可以用迭代器来实现。

 

12.Collection 和 Collections 的区别:

(1) java.util.Collection 是一个集合接口(集合类的一个顶级接口)。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式,其直接继承接口有List与Set。

 Collection
├List
│├LinkedList
│├ArrayList
│└Vector
│ └Stack
└Set

(2) java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于Java的Collection框架。

package com.guigu.collection;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class TestCollections {
     public static void main(String args[]) {
           // 注意List是实现Collection接口的
           List list = new ArrayList();
           double array[] = { 112, 111, 23, 456, 231 };
           for (int i = 0; i < array.length; i++) {
                list.add(new Double(array[i]));
           }
           Collections.sort(list);
           for (int i = 0; i < array.length; i++) {
                System.out.println(list.get(i));
           }
           // 结果:23.0 111.0 112.0 231.0 456.0
     }
}

 

13.Java 集合与数组相互转换

   //使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
   //说明:使用toArray带参方法,入参分配的数组空间不够大时,toArray方法内部将重新分配内存空间,并返回新数组地址;如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
   public static void collectionToArray(){
      List<String> list= new ArrayList<String>(2);
      list.add("Tom");
      list.add("Jeety");
      System.out.println("list"+list);
      String[] array = new String[list.size()];
      array=list.toArray(array);
      for (String string : array) {
         System.out.println("array"+string);
      }
   }
   
   //不要在foreach循环里进行元素的remove/add操作。
   //remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
   public static void traverse(){
      ArrayList<String> a  = new ArrayList<String>();
      a.add("AA");
      a.add("BB");
      System.out.println("a"+a);
      Iterator<String> it = a.iterator();
      while (it.hasNext()) {
      String temp = it.next();
      it.remove();
      System.out.println(temp);
      }
   }
   
}

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员学习圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值