zoukankan      html  css  js  c++  java
  • Java集合详解

    一、数组和集合的比较

    数组不是面向对象的,存在明显的缺陷,集合弥补了数组的缺点,比数组更灵活更实用,而且不同的集合框架类可适用不同场合。如下:
    1:数组能存放基本数据类型和对象,而集合类存放的都是对象的引用,而非对象本身!
    2:数组容易固定无法动态改变,集合类容量动态改变。 
    3:数组无法判断其中实际存有多少元素,length只告诉了数组的容量,而集合的size()可以确切知道元素的个数 
    4:集合有多种实现方式和不同适用场合,不像数组仅采用顺序表方式 
    5:集合以类的形式存在,具有封装、继承、多态等类的特性,通过简单的方法和属性即可实现各种复杂操作,大大提高了软件的开发效率

    二、Java集合

    Collection和Map,是集合框架的根接口。

    Collection的子接口:

    Set:接口 ---实现类: HashSet、LinkedHashSet
       Set的子接口SortedSet接口---实现类:TreeSet
    List:接口---实现类: LinkedList,Vector,ArrayList 

    List集合

    有序列表,允许存放重复的元素; 
    实现类: 
    ArrayList:数组实现,查询快,增删慢,轻量级;(线程不安全)
    LinkedList:双向链表实现,增删快,查询慢 (线程不安全)
    Vector:数组实现,重量级  (线程安全、使用少)

    ArrayList

    底层是Object数组,所以ArrayList具有数组的查询速度快的优点以及增删速度慢的缺点。

    而在LinkedList的底层是一种双向循环链表。在此链表上每一个数据节点都由三部分组成:前指针(指向前面的节点的位置),数据,后指针(指向后面的节点的位置)。最后一个节点的后指针指向第一个节点的前指针,形成一个循环。

    双向循环链表的查询效率低但是增删效率高。

    ArrayList和LinkedList在用法上没有区别,但是在功能上还是有区别的。

    LinkedList

    LinkedList是采用双向循环链表实现的。
    利用LinkedList实现栈(stack)、队列(queue)、双向队列(double-ended queue )。
    它具有方法addFirst()、addLast()、getFirst()、getLast()、removeFirst()、removeLast()等。

    经常用在增删操作较多而查询操作很少的情况下:

    队列和堆栈。

    队列:先进先出的数据结构。

    栈:后进先出的数据结构。

    注意:使用栈的时候一定不能提供方法让不是最后一个元素的元素获得出栈的机会。

    Vector

    (与ArrayList相似,区别是Vector是重量级的组件,使用使消耗的资源比较多。)

    结论:在考虑并发的情况下用Vector(保证线程的安全)。

    在不考虑并发的情况下用ArrayList(不能保证线程的安全)。

    List常用方法:
    void add(int index, Object element) :添加对象element到位置index上
    boolean addAll(int index, Collection collection) :在index位置后添加容器collection中所有的元素
    Object get(int index) :取出下标为index的位置的元素
    int indexOf(Object element) :查找对象element 在List中第一次出现的位置
    int lastIndexOf(Object element) :查找对象element 在List中最后出现的位置
    Object remove(int index) :删除index位置上的元素 
    ListIterator listIterator(int startIndex) :返回一个ListIterator 跌代器,开始位置为startIndex 
    List subList(int fromIndex, int toIndex) :返回一个子列表List ,元素存放为从 fromIndex 到toIndex之前的一个元素

     Set集合

    扩展Collection接口
    无序集合,不允许存放重复的元素;允许使用null元素
    对 add()、equals() 和 hashCode() 方法添加了限制
    HashSet和TreeSet是Set的实现
    Set—》hashSet linkedHashSet
    SortedSet —》 TreeSet

    HashSet 的后台有一个HashMap;初始化后台容量;只不过生成一个HashSet的话,系统只提供key的访问; 如果有两个Key重复,那么会覆盖之前的;

     
    实现类 :
    HashSet:equals返回true,hashCode返回相同的整数;哈希表;存储的数据是无序的。
    LinkedHashSet:此实现与 HashSet 的不同之外在于,后者维护着一个运行于所有条目的双重链接列表。存储的数据是有序的。

    HashSet类

    HashSet类直接实现了Set接口, 其底层其实是包装了一个HashMap去实现的。HashSet采用HashCode算法来存取集合中的元素,因此具有比较好的读取和查找性能。

    HashSet的特征

    • 不仅不能保证元素插入的顺序,而且在元素在以后的顺序中也可能变化(这是由HashSet按HashCode存储对象(元素)决定的,对象变化则可能导致HashCode变化)

    • HashSet是线程非安全的

    • HashSet元素值可以为NULL


    HashSet常用方法:
    public boolean contains(Object o) :如果set包含指定元素,返回true 
    public Iterator iterator()返回set中元素的迭代器 
    public Object[] toArray() :返回包含set中所有元素的数组public Object[] toArray(Object[] a) :返回包含set中所有元素的数组,返回数组的运行时类型是指定数组的运行时类型
    public boolean add(Object o) :如果set中不存在指定元素,则向set加入
    public boolean remove(Object o) :如果set中存在指定元素,则从set中删除 
    public boolean removeAll(Collection c) :如果set包含指定集合,则从set中删除指定集合的所有元素 
    public boolean containsAll(Collection c) :如果set包含指定集合的所有元素,返回true。如果指定集合也是一个set,只有是当前set的子集时,方法返回true

    实现Set接口的HashSet,依靠HashMap来实现的。
    我们应该为要存放到散列表的各个对象定义hashCode()和equals()。

    HashSet的equals和HashCode

    前面说过,Set集合是不允许重复元素的,否则将会引发各种奇怪的问题。那么HashSet如何判断元素重复呢?

    HashSet需要同时通过equals和HashCode来判断两个元素是否相等,具体规则是,如果两个元素通过equals为true,并且两个元素的hashCode相等,则这两个元素相等(即重复)。

    所以如果要重写保存在HashSet中的对象的equals方法,也要重写hashCode方法,重写前后hashCode返回的结果相等(即保证保存在同一个位置)。所有参与计算 hashCode() 返回值的关键属性,都应该用于作为 equals() 比较的标准。

    LinkedHashSet的特征

    LinkedHashSet是HashSet的一个子类,LinkedHashSet也根据HashCode的值来决定元素的存储位置,但同时它还用一个链表来维护元素的插入顺序,插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。

    在JAVA7中, LinkedHashSet没有定义任何方法,只有四个构造函数,它的构造函数调用了父类(HashSet)的带三个参数的构造方法

    由此可知,LinkedHashSet本质上也是从LinkedHashMap而来,LinkedHashSet的所有方法都继承自HashSet, 而它能维持元素的插入顺序的性质则继承自LinkedHashMap.

    TreeSet类的特征

    TreeSet实现了SortedSet接口,顾名思义这是一种排序的Set集合,查看jdk源码发现底层是用TreeMap实现的,本质上是一个红黑树原理。 正因为它是排序了的,所以相对HashSet来说,TreeSet提供了一些额外的按排序位置访问元素的方法,例如first(), last(), lower(), higher(), subSet(), headSet(), tailSet().

    TreeSet的排序分两种类型,一种是自然排序,另一种是定制排序。

    自然排序(在元素中写排序规则)

    TreeSet 会调用compareTo方法比较元素大小,然后按升序排序。所以自然排序中的元素对象,都必须实现了Comparable接口,否则会跑出异常。对于TreeSet判断元素是否重复的标准,也是调用元素从Comparable接口继承而来额compareTo方法,如果返回0则是重复元素(两个元素I相等)。Java的常见类都已经实现了Comparable接口。

    还有个重要问题是,因为TreeSet会调用元素的compareTo方法,这就要求所有元素的类型都相同,否则也会发生异常。也就是说,TreeSet只允许存入同一类的元素

    定制排序(在集合中写排序规则)

    TreeSet还有一种排序就是定制排序,定制排序时候,需要关联一个 Comparator对象,由Comparator提供排序逻辑。

    TreeSet是依靠TreeMap来实现的。
    TreeSet是一个有序集合,TreeSet中元素将按照升序排列,缺省是按照自然顺序进行排列,意味着TreeSet中元素要实现Comparable接口
    我们可以在构造TreeSet对象时,传递实现了Comparator接口的比较器对象。

    Comparable和Comparator 
    Comparable 接口以提供自然排序顺序。
    对于那些没有自然顺序的类、或者当您想要一个不同于自然顺序的顺序时,您可以实现 
    Comparator 接口来定义您自己的排序函数。可以将Comparator传递给Collections.sort或Arrays.sort。

    Comparator接口 
    当一个类并未实现Comparable,或者不喜欢缺省的Comaparable行为。可以实现Comparator接口
    直接实现Comparator的compare接口完成自定义比较类。
    例:Arrays.sort(results, new Comparator<RepDataQueryResultVO>() 数组排序 RepDataQueryExecutor
    例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO>()

    EnumSet特征

    EnumSet顾名思义就是专为枚举类型设计的集合,因此集合元素必须是枚举类型,否则会抛出异常。 EnumSet集合也是有序的,其顺序就是Enum类内元素定义的顺序。EnumSet存取的速度非常快,批量操作的速度也很快。EnumSet主要提供以下方法,allOf, complementOf, copyOf, noneOf, of, range等。注意到EnumSet并没有提供任何构造函数,要创建一个EnumSet集合对象,只需要调用allOf等方法。

    几种Set的比较:
    HashSet外部无序地遍历成员。 成员可为任意Object子类的对象,但如果覆盖了equals方法,同时注意修改hashCode方法。 

    TreeSet外部有序地遍历成员; 附加实现了SortedSet, 支持子集等要求顺序的操作 成员要求实现Comparable接口,或者使用Comparator构造TreeSet。成员一般为同一类型。 

    LinkedHashSet外部按成员的插入顺序遍历成员 成员与HashSet成员类似 

    HashSet是基于Hash算法实现的,其性能通常都优于TreeSet。我们通常都应该使用HashSet,在我们需要排序的功能时,我们才使用TreeSet。

    HashSet的元素存放顺序和我们添加进去时候的顺序没有任何关系,而LinkedHashSet 则保持元素的添加顺序。TreeSet则是对我们的Set中的元素进行排序存放。

    一般来说,当您要从集合中以有序的方式抽取元素时,TreeSet 实现就会有用处。为了能顺利进行,添加到 TreeSet 的元素必须是可排序的。 而您同样需要对添加到TreeSet中的类对象实现 Comparable 接口的支持。一般说来,先把元素添加到 HashSet,再把集合转换为 TreeSet 来进行有序遍历会更快。

    各种Set集合性能分析

    • HashSet和TreeSet是Set集合中用得最多的I集合。HashSet总是比TreeSet集合性能好,因为HashSet不需要额维护元素的顺序。

    • LinkedHashSet需要用额外的链表维护元素的插入顺序,因此在插入时性能比HashSet低,但在迭代访问(遍历)时性能更高。因为插入的时候即要计算hashCode又要维护链表,而遍历的时候只需要按链表来访问元素。

    • EnumSet元素是所有Set元素中性能最好的,但是它只能保存Enum类型的元素

    Map

    集合框架的第二类接口树。
    它提供了一组键值的映射。其中存储的每个对象都有一个相应的关键字(key),关键字决定了对象在Map中的存储位置。
    关键字应该是唯一的,每个key 只能映射一个value。

    实现类:

    HashMap、TreeMap、LinkedHashMap、Hashtable等
    HashMap:键值对,key不能重复,但是value可以重复;key的实现就是HashSet;value对应着放;允许null的键或值;
    Hashtable:线程安全的,不允许null的键或值;
    Properties::key和value都是String类型,用来读配置文件;
    TreeMap:对key排好序的Map; key 就是TreeSet, value对应每个key; key要实现Comparable接口或TreeMap有自己的构造器; 
    LinkedHashMap: 此实现与 HashMap 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。存储的数
    据是有序的。


    HashMap:
    Map 主要用于存储键(key)值(value)对,根据键得到值,因此键不允许重复,但允许值重复。
    HashMap 是一个最常用的Map,它根据键的HashCode 值存储数据,根据键可以直接获取它的值,具有很快的访问速度。
    HashMap最多只允许一条记录的键为Null;允许多条记录的值为 Null;
    HashMap不支持线程的同步,即任一时刻可以有多个线程同时写HashMap;可能会导致数据的不一致。如果需要同步,可以用 Collections的synchronizedMap方法使HashMap具有同步的能力。

    HashMap实现原理---散列
    Hash哈希算法的意义在于提供了一种快速存取数据的方法,它用一种算法建立键值与真实值之间的对应关系。散列表又称为哈希表。散列表算法的基本思想是:以结点的关键字为自变量,通过一定的函数关系(散列函数)计算出对应的函数值,以这个值作为该结点存储在散列表中地址。
    当散列表中的元素存放太满,就必须进行再散列,将产生一个新的散列表,所有元素存放到新的散列表中,原先的散列表将被删除。在Java语言中,通过负载因子(load factor)来决定何时对散列表进行再散列。例如:如果负载因子0.75,当散列表中已经有75%位置已经放满,那么将进行再散列。
    负载因子越高(越接近1.0),内存的使用效率越高,元素的寻找时间越长。负载因子越低(越接近0.0),元素的寻找时间越短,内存浪费越多。


    Map集合比较:
    HashMap的存入顺序和输出顺序无关。
    LinkedHashMap 则保留了键值对的存入顺序。
    TreeMap则是对Map中的元素进行排序。
    因为HashMap和LinkedHashMap 存储数据的速度比直接使用TreeMap 要快,存取效率要高。
    当完成了所有的元素的存放后,我们再对整个的Map中的元素进行排序。这样可以提高整个程序的运行的效率,缩短执行时间。
    注意:TreeMap中是根据键(Key)进行排序的。而如果我们要使用TreeMap来进行正常的排序的话,Key 中存放的对象必须实现Comparable 接口。

    Map常用方法:
    Object put(Object key,Object value):用来存放一个键-值对Map中 
    Object remove(Object key):根据key(键),移除键-值对,并将值返回
    void putAll(Map mapping) :将另外一个Map中的元素存入当前的Map中
    void clear() :清空当前Map中的元素
    Object get(Object key) :根据key(键)取得对应的值
    boolean containsKey(Object key) :判断Map中是否存在某键(key)
    boolean containsValue(Object value):判断Map中是否存在某值(value) 
    public Set keySet() :返回所有的键(key),并使用Set容器存放
    public Collection values() :返回所有的值(Value),并使用Collection存放
    public Set entrySet() :返回一个实现 Map.Entry 接口的元素 Set

    总结与面试

    1.ArrayList: 元素单个,效率高,多用于查询

    2.Vector:    元素单个,线程安全,多用于查询

    3.LinkedList:元素单个,多用于插入和删除

    4.HashMap:   元素成对,元素可为空

    5.HashTable: 元素成对,线程安全,元素不可为空

    HashMap和Hashtable的区别:

    HashMap和Hashtable都是java的集合类,都可以用来存放java对象,这是他们的相同点

    以下是他们的区别:

    1.历史原因:

    Hashtable是基于陈旧的Dictionary类的,HashMap是java 1.2引进的Map接口的一个现实。

    2.同步性:

    Hashtable是同步的,这个类中的一些方法保证了Hashtable中的对象是线程安全的,而HashMap则是异步的,因此HashMap中的对象并不是线程安全的,因为同步的要求会影响执行的效率,所以如果你不需要线程安全的结合那么使用HashMap是一个很好的选择,这样可以避免由于同步带来的不必要的性能开销,从而提高效率,我们一般所编写的程序都是异步的,但如果是服务器端的代码除外。

    3.值:

    HashMap可以让你将空值作为一个表的条目的key或value

    Hashtable是不能放入空值(null)的

    ArrayList和Vector的区别:

    ArrayList与Vector都是java的集合类,都是用来存放java对象,这是他们的相同点,

    区别:

    1.同步性:

    Vector是同步的,这个类的一些方法保证了Vector中的对象的线程安全的,而ArrayList则是异步的,因此ArrayList中的对象并不 是线程安全的,因为同步要求会影响执行的效率,所以你不需要线程安全的集合那么使用ArrayList是一个很好的选择,这样可以避免由于同步带来的不必 要的性能开销。

    2.数据增长:

    从内部实现的机制来讲,ArrayList和Vector都是使用数组(Array)来控制集合中的对象,当你向两种类型中增加元素的时候,如果元素的数目超过了内部数组目前的长度他们都需要扩展内部数组的长度,Vector缺省情况下自动增长原来一倍的数组长度,ArrayList是原来的50%,所以最后你获得的这个集合所占的空间总是比你实际需要的要大,所以如果你要在集合中保存大量的数据,那么使用Vector有一些优势,因为你可以通过设置集合的初始大小来避免不必要的资源开销。

    总结:

    1)如果要求线程安全,使用Vector,Hashtable

    2)如果不要求线程安全,使用ArrayList,LinkedList,HashMap

    3)如果要求键值对,则使用HashMap,Hashtable

    4)如果数据量很大,又要求线程安全考虑Vector

    arraylist和linkedlist联系与区别
    1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
    2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
    3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。 这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。

    HashMap与TreeMap联系与区别
    1、 HashMap通过hashcode对其内容进行快速查找,而TreeMap中所有的元素都保持着某种固定的顺序,如果你需要得到一个有序的结果你就应该使用TreeMap(HashMap中元素的排列顺序是不固定的)。
    2、在Map 中插入、删除和定位元素,HashMap是最好的选择。但如果您要按自然顺序或自定义顺序遍历键,那么TreeMap会更好。使用HashMap要求添加的键类明确定义了hashCode()和 equals()的实现。
    两个map中的元素一样,但顺序不一样,导致hashCode()不一样。

    同样做测试:
    在HashMap中,同样的值的map,顺序不同,equals时,false;
    而在treeMap中,同样的值的map,顺序不同,equals时,true,说明,treeMap在equals()时是整理了顺序了的。

  • 相关阅读:
    一文总结十大经典排序算法(思维导图 + 动图演示 + 代码实现 C/C++/Python + 致命吐槽)
    VulnHub——Kioptrix Level 2
    史上最全Redis面试题(2020最新版)
    js 根据秒数获取多少小时,多少分钟,多少秒
    RabbitMQ的死信队列
    女朋友也能看懂的多线程同步
    RabbitMQ的备份交换器
    BI Publisher(rtf)模板开发语法大全
    修改CUSTOM.PLL文件调用客户化FORM&修改标准FORM
    EBS客户化迁移SQL
  • 原文地址:https://www.cnblogs.com/deityjian/p/11409696.html
Copyright © 2011-2022 走看看