Java集合:整体结构

一、Java中集合

  Java中集合类是Java编程中使用最频繁、最方便的类。集合类作为容器类可以存储任何类型的数据,当然也可以结合泛型存储指定的类型(不过泛型仅仅在编译期有效,运行时是会被擦除的)。集合类中存储的仅仅是对象的引用,并不存储对象本身。集合类的容量可以在运行期间进行动态扩展,并且还提供很多很方便的方法,如求集合的并集、交集等。

二、集合类结构

  Java中的集合包含多种数据结构,如链表、队列、哈希表等。从类的继承结构来说,可以分为两大类,一类是继承自Collection接口,这类集合包含List、Set和Queue等集合类。另一类是继承自Map接口,这主要包含了哈希表相关的集合类。下面我们看一下这两大类的继承结构图:

1、List、Set和Queue

图中的绿色的虚线代表实现,绿色实线代表接口之间的继承,蓝色实线代表类之间的继承。

   (1)List:我们用的比较多List包括ArrayList和LinkedList,这两者的区别也很明显,从其名称上就可以看出。ArrayList的底层的通过数组实现,所以其随机访问的速度比较快,但是对于需要频繁的增删的情况,效率就比较低了。而对于LinkedList,底层通过链表来实现,所以增删操作比较容易完成,但是对于随机访问的效率比较低。

我们先看下两者的插入效率:

 1 package com.paddx.test.collection;
 2
 3 import java.util.ArrayList;
 4 import java.util.LinkedList;
 5
 6 public class ListTest {
 7     public static void main(String[] args) {
 8         for(int i=0;i<10000;i++){
 9
10         }
11         long start = System.currentTimeMillis();
12
13         LinkedList<Integer> linkedList = new LinkedList<Integer>();
14         for(int i=0;i<100000;i++){
15             linkedList.add(0,i);
16         }
17
18         long end = System.currentTimeMillis();
19         System.out.println(end - start);
20
21         ArrayList<Integer> arrayList = new ArrayList<Integer>();
22         for(int i=0;i<100000;i++){
23             arrayList.add(0,i);
24         }
25
26         System.out.println(System.currentTimeMillis() - end);
27     }
28 }

下面是本地执行的结果:

23
1227

  可以看出,在这种情况下,LinkedList的插入效率远远高于ArrayList,当然这是一种比较极端的情况。我们再来比较一下两者随机访问的效率:

 1 package com.paddx.test.collection;
 2
 3 import java.util.ArrayList;
 4 import java.util.LinkedList;
 5 import java.util.Random;
 6
 7 public class ListTest {
 8     public static void main(String[] args) {
 9
10         Random random = new Random();
11
12         for(int i=0;i<10000;i++){
13
14         }
15         LinkedList<Integer> linkedList = new LinkedList<Integer>();
16         for(int i=0;i<100000;i++){
17             linkedList.add(i);
18         }
19
20         ArrayList<Integer> arrayList = new ArrayList<Integer>();
21         for(int i=0;i<100000;i++){
22             arrayList.add(i);
23         }
24
25         long start = System.currentTimeMillis();
26
27
28         for(int i=0;i<100000;i++){
29             int j = random.nextInt(i+1);
30             int k = linkedList.get(j);
31         }
32
33         long end = System.currentTimeMillis();
34         System.out.println(end - start);
35
36         for(int i=0;i<100000;i++){
37             int j = random.nextInt(i+1);
38             int k = arrayList.get(j);
39         }
40
41         System.out.println(System.currentTimeMillis() - end);
42     }
43 }

下面是我本机执行的结果:

5277
6

  很明显可以看出,ArrayList的随机访问效率比LinkedList高出好几个数量级。通过这两段代码,我们应该能够比较清楚的知道LinkedList和ArrayList的区别和适应的场景。至于Vector,它是ArrayList的线程安全版本,而Stack则对应栈数据结构,这两者用的比较少,这里就不举例了。

  (2)Queue:一般可以直接使用LinkedList完成,从上述类图也可以看出,LinkedList继承自Deque,所以LinkedList具有双端队列的功能。PriorityQueue的特点是为每个元素提供一个优先级,优先级高的元素会优先出队列。

  (3)Set:Set与List的主要区别是Set是不允许元素重复的,而List则可以允许元素重复的。判断元素的重复需要根据对象的hash方法和equals方法来决定。这也是我们通常要为集合中的元素类重写hashCode方法和equals方法的原因。我们还是通过一个例子来看一下Set和List的区别,以及hashcode方法和equals方法的作用:

package com.paddx.test.collection;

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

public class SetTest {

    public static void main(String[] args) {
        Person p1 = new Person("lxp",10);
        Person p2 = new Person("lxp",10);
        Person p3 = new Person("lxp",20);

        ArrayList<Person> list = new ArrayList<Person>();
        list.add(p1);
        System.out.println("---------");
        list.add(p2);
        System.out.println("---------");
        list.add(p3);
        System.out.println("List size=" + list.size());

        System.out.println("----分割线-----");

        Set<Person> set = new HashSet<Person>();
        set.add(p1);
        System.out.println("---------");
        set.add(p2);
        System.out.println("---------");
        set.add(p3);
        System.out.println("Set size="+set.size());
    }

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

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

        @Override
        public boolean equals(Object o) {
            System.out.println("Call equals();name="+name);
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Person person = (Person) o;

            return name.equals(person.name);

        }

        @Override
        public int hashCode() {
            System.out.println("Call hashCode(),age="+age);
            return age;
        }
    }
}

  上述代码的执行结果如下:

---------
---------
List size=3
----分割线-----
Call hashCode(),age=10
---------
Call hashCode(),age=10
Call equals();name=lxp
---------
Call hashCode(),age=20
Set size=2

  从结果看出,元素加入List的时候,不执行额外的操作,并且可以重复。而加入Set之前需要先执行hashCode方法,如果返回的值在集合中已存在,则要继续执行equals方法,如果equals方法返回的结果也为真,则证明该元素已经存在,会将新的元素覆盖老的元素,如果返回hashCode值不同,则直接加入集合。这里记住一点,对于集合中元素,hashCode值不同的元素一定不相等,但是不相等的元素,hashCode值可能相同。

  HashSet和LinkedHashSet的区别在于后者可以保证元素插入集合的元素顺序与输出顺序保持一致。而TresSet的区别在于其排序是按照Comparator来进行排序的,默认情况下按照字符的自然顺序进行升序排列。

  (4)Iterable:从这个图里面可以看到Collection类继承自Iterable,该接口的作用是提供元素遍历的功能,也就是说所有的集合类(除Map相关的类)都提供元素遍历的功能。Iterable里面包含了Iterator的迭代器,其源码如下,大家如果熟悉迭代器模式的话,应该很容易理解。

1 public interface Iterator<E> {
2
3     boolean hasNext();
4
5     E next();
6
7     void remove();
8 }

2、Map:

Map类型的集合最大的优点在于其查找效率比较高,理想情况下可以实现O(1)的时间复杂度。Map中最常用的是HashMap,LinkedHashMap与HashMap的区别在于前者能够保证插入集合的元素顺序与输出顺序一致。这两者与TreeMap的区别在于TreeMap是根据键值进行排序的,当然其底层的实现也有本质的区别,如HashMap底层是一个哈希表,而TreeMap的底层数据结构是一棵树。我们现在看下TreeMap与LinkedHashMap的区别:

package com.paddx.test.collection;

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

public class MapTest {
    public static void main(String[] args) {
        Map<String,String> treeMap = new TreeMap<String,String>();
        Map<String,String> linkedMap = new LinkedHashMap<String, String>();

        treeMap.put("b",null);
        treeMap.put("c",null);
        treeMap.put("a",null);

        for (Iterator<String> iter = treeMap.keySet().iterator();iter.hasNext();){
            System.out.println("TreeMap="+iter.next());
        }

        System.out.println("----------分割线---------");

        linkedMap.put("b",null);
        linkedMap.put("c",null);
        linkedMap.put("a",null);

        for (Iterator<String> iter = linkedMap.keySet().iterator();iter.hasNext();){
            System.out.println("LinkedHashMap="+iter.next());
        }
    }
}

运行上述代码,执行结果如下:

TreeMap=a
TreeMap=b
TreeMap=c
----------分割线---------
LinkedHashMap=b
LinkedHashMap=c
LinkedHashMap=a

  从运行结果可以很明显的看出这TreeMap和LinkedHashMap的区别,前者是按字符串排序进行输出的,而后者是根据插入顺序进行输出的。细心的读者可以发现,HashMap与TreeMap的区别,与之前提到的HashSet与TreeSet的区别是一致的,在后续进行源码分析的时候,我们可以看到HashSet和TreeSet本质上分别是通过HashMap和TreeMap来实现的,所以它们的区别自然也是相同的。HashTable现在已经很少使用了,与HashMap的主要区别是HashTable是线程安全的,不过由于其效率比较低,所以通常使用HashMap,在多线程环境下,通常用CurrentHashMap来代替。

三、总结

  本文只是从整体上介绍了Java集合框架及其继承关系。除了上述类,集合还提供Collections和Arrays两个工具类,此外,集合中排序跟Comparable和Comparator紧密相关。在之后的文章中将对上述提的类在JDK中实现源码进行详细分析。

时间: 2024-11-08 20:53:56

Java集合:整体结构的相关文章

解析java集合框架

在Java语言中,Java语言的设计者对常用的数据结构和算法做了一些规范(接口)和实现(具体实现接口的类).所有抽象出来的数据结构和操作(算法)统称为Java集合框架(Java Collection Framework). Java程序员在具体应用时,不必考虑数据结构和算法实现细节,只需要用这些类创建出来一些对象,然后直接应用就可以了.这样就大大提高了编程效率. Java类集框架的优势:        1) 这种框架是高性能的.对基本类集(动态数组,链接表,树和散列表)的实现是高效率的.一般很少

1.Java集合-HashMap实现原理及源码分析

哈希表(Hash  Table)也叫散列表,是一种非常重要的数据结构,应用场景及其丰富,许多缓存技术(比如memcached)的核心其实就是在内存中维护一张大的哈希表,而HashMap的实现原理也常常出现在各类的面试题中,这里对java集合框架中的对应实现HashMap的实现原理进行讲解,然后对JDK7的HashMap的源码进行分析 哈希算法,是一类算法: 哈希表(Hash  Table)是一种数据结构: 哈希函数:是支撑哈希表的一类函数: HashMap 是 Java中用哈希数据结构实现的Ma

Java集合(1)一 集合框架

目录 Java集合(1)一 集合框架 Java集合(2)一 ArrayList 与 LinkList Java集合(3)一 红黑树.TreeMap与TreeSet(上) java集合(4)一 红黑树.TreeMap与TreeSet(下) Java集合(5)一 HashMap与HashSet 引言 集合在任何语言中都是比较重要的基础知识,不同的集合在实现上采用了各种不同的数据结构,导致了各个集合的性能以及使用方式上存在很大差异,深入了解集合框架的整体结构以及各个集合类的实现原理,并灵活使用各个集合

Java—集合框架List

集合的概念 现实生活中:很多的事物凑在一起 数学中的集合:具有共同属性的事物的总和 Java中的集合类:是一种工具类,就像是容器,存储任意数量的具有共同属性的对象 集合的作用 在类的内部,对数据进行组织(针对作用与意义一样的属性,将他们放到一个集合中) 简单而快速的搜索大数量的条目 有的集合接口,提供了一系列排列有序的元素,并且可以在序列中快速的插入或删除有关元素 有的集合接口,提供了映射关系,可以通过关键字(key)去快速查找到对应的唯一对象,而这个关键字可以是任意类型 与数组相比 数组的长度

Java 集合

在Java Collections Framework中,不同类型的集合使用不同类型的数据结构以不同的方式存储它们的元素. 集合框架提供了遍历集合的以下方法: 使用迭代器 使用for-each循环 使用forEach()方法 使用迭代器 迭代器可以对集合执行以下三个操作: 检查是否有尚未访问的元素. hasNext() 检查是否有下一个访问的元素. next() 删除集合的最后访问元素. remove() 例子1 使用迭代器打印列表的所有元素: import java.util.ArrayLis

《深入理解Java集合框架》系列文章

Introduction 关于C++标准模板库(Standard Template Library, STL)的书籍和资料有很多,关于Java集合框架(Java Collections Framework, JCF)的资料却很少,甚至很难找到一本专门介绍它的书籍,这给Java学习者们带来不小的麻烦.我深深的不解其中的原因.虽然JCF设计参考了STL,但其定位不是Java版的STL,而是要实现一个精简紧凑的容器框架,对STL的介绍自然不能替代对JCF的介绍. 本系列文章主要从数据结构和算法层面分析

Java集合相关面试问题和答案

Java集合相关面试问题和答案 面试试题 1.Java集合框架是什么?说出一些集合框架的优点? 每种编程语言中都有集合,最初的Java版本包含几种集合类:Vector.Stack.HashTable和Array.随着集合的广泛使用,Java1.2提出了囊括所有集合接口.实现和算法的集合框架.在保证线程安全的情况下使用泛型和并发集合类,Java已经经历了很久.它还包括在Java并发包中,阻塞接口以及它们的实现.集合框架的部分优点如下: (1)使用核心集合类降低开发成本,而非实现我们自己的集合类.

Java集合

JAVA集合小结   有序否 允许元素重复否 Collection 否 是 List 是 是 Set AbstractSet 否 否 HashSet TreeSet 是(用二叉树排序) Map AbstractMap 否 使用key-value来映射和存储数据,Key必须惟一,value可以重复 HashMap TreeMap 是(用二叉树排序) 几个面试常见问题:1.Q:ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?   A:Vector和HashT

一大波Java来袭(五)——Java集合概述

把"Java集合"比作是容器,可以把多个对象(实际:是对象的引用),扔在容器中. JDK1.5之前,被丢进集合中的对象,会丢失起数据类型.默认为Object类型 JDK1.5之后,不会丢失数据类型,因为引入了"泛型"(作为下文重点) 本文将结合数据结构,重点讲述Java的4种集合体系的功能和用法,并给出不同应用场景改如何选择? 一.同样是用于"存储数据",既可以使用"数组",有可以使用"集合",有什么区别吗