精华内容
下载资源
问答
  • 集合框架知识点总结

    2018-11-24 17:30:52
    说明:对于以上框架图有如下几说明 1.所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架根接口,这两个接口又包含了一些子接口或实现类...

    简化图:

    说明:对于以上的框架图有如下几点说明

    1.所有集合类都位于java.util包下。Java的集合类主要由两个接口派生而出:CollectionMap,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。
    2. 集合接口:6个接口(短虚线表示),表示不同集合类型,是集合框架的基础。
    3. 抽象类:5个抽象类(长虚线表示),对集合接口的部分实现。可扩展为自定义集合类。
    4. 实现类:8个实现类(实线表示),对接口的具体实现。
    5. Collection 接口是一组允许重复的对象。
    6. Set 接口继承 Collection,集合元素不重复。
    7. List 接口继承 Collection,允许重复,维护元素插入顺序。
    8. Map接口是键-值对象,与Collection接口没有什么关系。
    9.Set、List和Map可以看做集合的三大类:
    List集合是有序集合,集合中的元素可以重复,访问集合中的元素可以根据元素的索引来访问。
    Set集合是无序集合,集合中的元素不可以重复,访问集合中的元素只能根据元素本身来访问(也是集合里元素不允许重复的原因)。
    Map集合中保存Key-value对形式的元素,访问时只能根据每项元素的key来访问其value。

    二、总体分析

    大致说明:
    看上面的框架图,先抓住它的主干,即Collection和Map

    1、Collection是一个接口,是高度抽象出来的集合,它包含了集合的基本操作和属性。Collection包含了List和Set两大分支。
    (1)List是一个有序的队列,每一个元素都有它的索引。第一个元素的索引值是0。List的实现类有LinkedList, ArrayList, Vector, Stack。

    (2)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。它们是操作数组、集合的两个工具类。

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

    三、Collection接口

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

    其中,有几个比较常用的方法,比如方法add()添加一个元素到集合中,addAll()将指定集合中的所有元素添加到集合中,contains()方法检测集合中是否包含指定的元素,toArray()方法返回一个表示集合的数组。

    另外,Collection中有一个iterator()函数,它的作用是返回一个Iterator接口。通常,我们通过Iterator迭代器来遍历集合。ListIterator是List接口所特有的,在List接口中,通过ListIterator()返回一个ListIterator对象。

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

    1.List接口

    List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引。List集合允许使用重复元素,可以通过索引来访问指定位置的集合元素。

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

    List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

    (1)ArrayList

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

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

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

    (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(...));

    (3)Vector

          与ArrayList相似,但是Vector是同步的。所以说Vector是线程安全的动态数组。它的操作与ArrayList几乎一样。

    (4)Stack

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

    2.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决定的,其具体位置其实是固定的。

         此外需要说明一点,在set接口中的不重复是有特殊要求的。
         举一个例子:对象A和对象B,本来是不同的两个对象,正常情况下它们是能够放入到Set里面的,但是如果对象A和B的都重写了hashcode和equals方法,并且重写后的hashcode和equals方法是相同的话。那么A和B是不能同时放入到Set集合中去的,也就是Set集合中的去重和hashcode与equals方法直接相关。 

      为了更好地理解,请看下面的例子:

    复制代码

    public class Test{ 
    public static void main(String[] args) { 
         Set<String> set=new HashSet<String>(); 
         set.add("Hello"); 
         set.add("world"); 
         set.add("Hello"); 
         System.out.println("集合的尺寸为:"+set.size()); 
         System.out.println("集合中的元素为:"+set.toString()); 
      } 
    } 

    复制代码

    运行结果:

    集合的尺寸为:2
    集合中的元素为:[world, Hello]

    分析:由于String类中重写了hashcode和equals方法,用来比较指向的字符串对象所存储的字符串是否相等。所以这里的第二个Hello是加不进去的。

    再看一个例子:

    复制代码

    public class TestSet {
        
        public static void main(String[] args){
            
            Set<String> books = new HashSet<String>();
            //添加一个字符串对象
            books.add(new String("Struts2权威指南"));
            
            //再次添加一个字符串对象,
            //因为两个字符串对象通过equals方法比较相等,所以添加失败,返回false
            boolean result = books.add(new String("Struts2权威指南"));
            
            System.out.println(result);
            
            //下面输出看到集合只有一个元素
            System.out.println(books);    
    
        }
    }

    复制代码

    运行结果:

    false
    [Struts2权威指南]

    说明:程序中,book集合两次添加的字符串对象明显不是一个对象(程序通过new关键字来创建字符串对象),当使用==运算符判断返回false,使用equals方法比较返回true,所以不能添加到Set集合中,最后只能输出一个元素。

    (1)HashSet

         HashSet 是一个没有重复元素的集合。它是由HashMap实现的不保证元素的顺序(这里所说的没有顺序是指:元素插入的顺序与输出的顺序不一致),而且HashSet允许使用null 元素。HashSet是非同步的,如果多个线程同时访问一个哈希set,而其中至少一个线程修改了该set,那么它必须保持外部同步。 HashSet按Hash算法来存储集合的元素,因此具有很好的存取和查找性能。

    HashSet的实现方式大致如下,通过一个HashMap存储元素,元素是存放在HashMap的Key中,而Value统一使用一个Object对象。

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

    a.HashSet中存放null值
      HashSet中是允许存入null值的,但是在HashSet中仅仅能够存入一个null值。

    b.HashSet中存储元素的位置是固定的
      HashSet中存储的元素的是无序的,这个没什么好说的,但是由于HashSet底层是基于Hash算法实现的,使用了hashcode,所以HashSet中相应的元素的位置是固定的。
      
    c.必须小心操作可变对象(Mutable Object)。如果一个Set中的可变元素改变了自身状态导致Object.equals(Object)=true将导致一些问题。

    (2)LinkedHashSet

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

    (3)TreeSet

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

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

    四、Map接口

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

    1.HashMap

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

    2.LinkedHashMap

         LinkedHashMap是HashMap的一个子类,它保留插入的顺序,如果需要输出的顺序和输入时的相同,那么就选用LinkedHashMap。
         LinkedHashMap是Map接口的哈希表和链接列表实现,具有可预知的迭代顺序。此实现提供所有可选的映射操作,并允许使用null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变
         LinkedHashMap实现与HashMap的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可以是插入顺序或者是访问顺序
         根据链表中元素的顺序可以分为:按插入顺序的链表,和按访问顺序(调用get方法)的链表。默认是按插入顺序排序,如果指定按访问顺序排序,那么调用get方法后,会将这次访问的元素移至链表尾部,不断访问可以形成按访问顺序排序的链表。
         注意,此实现不是同步的。如果多个线程同时访问链接的哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。
         由于LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能,但在迭代访问Map里的全部元素时将有很好的性能,因为它以链表来维护内部顺序。

    3.TreeMap

         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。

    五、Iterator 与 ListIterator详解

    1.Iterator

    Iterator的定义如下:

    public interface Iterator<E> {}

    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] 

    注意:

    (1)Iterator只能单向移动。

    (2)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]

    复制代码

    六、异同点

    1.ArrayList和LinkedList

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

    2.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(枚举器)两种方式遍历。

    3.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

    复制代码

    4.HashSet、LinkedHashSet、TreeSet比较

    Set接口
    Set不允许包含相同的元素,如果试图把两个相同元素加入同一个集合中,add方法返回false。
    Set判断两个对象相同不是使用==运算符,而是根据equals方法。也就是说,只要两个对象用equals方法比较返回true,Set就不会接受这两个对象。

    HashSet
    HashSet有以下特点:
    ->  不能保证元素的排列顺序,顺序有可能发生变化。
    ->  不是同步的。
    ->  集合元素可以是null,但只能放入一个null。
        当向HashSet结合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值也相等。
        注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

    LinkedHashSet
        LinkedHashSet集合同样是根据元素的hashCode值来决定元素的存储位置,但是它同时使用链表维护元素的次序。这样使得元素看起来像是以插入顺序保存的,也就是说,当遍历该集合时候,LinkedHashSet将会以元素的添加顺序访问集合的元素。
        LinkedHashSet在迭代访问Set中的全部元素时,性能比HashSet好,但是插入时性能稍微逊色于HashSet。

    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)方法。

    复制代码

    package com.test;  
      
    import java.util.HashSet;  
    import java.util.LinkedHashSet;  
    import java.util.TreeSet;  
      
    /**  
     * @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]

     5、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数据结构的操作。其实,数组对象也可以用迭代器来实现。

    6、Collection 和 Collections区别

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

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

    (2)java.util.Collections 是一个包装类(工具类/帮助类)。它包含有各种有关集合操作的静态多态方法。此类不能实例化,就像一个工具类,用于对集合中元素进行排序、搜索以及线程安全等各种操作,服务于Java的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 
        } 
    } 
    展开全文
  • List(接口) ...实现了所有可选列表操作,并允许包括== null== 在内的所有元素。除了实现List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于Vector类,除了...

    List(接口)

    ArrayList(class、数组、随机访问、非线程安全)

    • Array的实现原理:动态数组。与Java中的数组相比,它的容量能动态增长。ArrayList 是 List接口的可变数组的实现。实现了所有可选列表操作,并允许包括== null== 在内的所有元素。除了实现List接口外,此类还提供一些方法来操作内部用来存储列表的数组的大小。(此类大致上等同于Vector类,除了此类是不同步的。)

    • 每个 ArrayList 实例都有一个容量,该容量是指用来存储列表元素的数组的大小。它总是至少等于列表的大小。随着向ArrayList中不断添加元素,其容量也自动增长。自动增长会带来数据向新数组的重新拷贝,因此,如果可预知数据量的多少,可在构造 ArrayList时指定其容量在添加大量元素前,应用程序也可以使用 ensureCapacity操作来增加ArrayLis实例的容量,这可以减少递增式再分配的数量。

    • 注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。(结构上的修改是指任何添加或删除一个或多个元素的操作,或者显式调整底层数组的大小;仅仅设置元素的值不是结构上的修改。)

    • 实现的接口

    public class ArrayList<E> extends AbstractList<E>
            implements List<E>, RandomAccess, Cloneable, java.io.Serializable
    {
    }
    
    • 底层使用数组实现
    /**
    * The array buffer into which the elements of the ArrayList are stored.
    * The capacity of the ArrayList is the length of this array buffer.
    */
    private transient Object[] elementData;
    
    • 构造方法
    /**
         * Constructs an empty list with an initial capacity of ten.
         */
        public ArrayList() {
            this(10);
        }
        /**
         * Constructs an empty list with the specified initial capacity.
         *
         * @param  initialCapacity  the initial capacity of the list
         * @throws IllegalArgumentException if the specified initial capacity
         *         is negative
         */
        public ArrayList(int initialCapacity) {
            super();
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal Capacity: "+
                                                   initialCapacity);
            this.elementData = new Object[initialCapacity];
        }
    
        /**
         * Constructs a list containing the elements of the specified
         * collection, in the order they are returned by the collection's
         * iterator.
         *
         * @param c the collection whose elements are to be placed into this list
         * @throws NullPointerException if the specified collection is null
         */
        public ArrayList(Collection<? extends E> c) {
            elementData = c.toArray();
            size = elementData.length;
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        }
    
    • 存储
    1. set(int index, E element):该方法首先调用==rangeCheck(index)==来校验 index 变量是否超出数组范围,超出则抛出异常。而后,取出原index位置的值,并且将新的 element 放入 Index 位置,返回 oldValue。
    public E set(int index, E element) {
            ==rangeCheck(index);==
    
            E oldValue = elementData(index);
            elementData[index] = element;
            return oldValue;
        }
        /**
          * Checks if the given index is in range.  If not, throws an appropriate
          * runtime exception.  This method does *not* check if the index is
          * negative: It is always used immediately prior to an array access,
          * which throws an ArrayIndexOutOfBoundsException if index is negative.
          */
          private void rangeCheck(int index) {
            if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
          }
    
    1. add(E e):该方法是将指定的元素添加到列表的尾部。当容量不足时,会调用 ==grow ==方法增长容量。
    public boolean add(E e) {
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            elementData[size++] = e;
            return true;
        }
        private void ensureCapacityInternal(int minCapacity) {
            modCount++;
            // overflow-conscious code
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = ==Arrays.copyOf==(elementData, newCapacity);
        }
    

    关于上述Arrays.copyof():Arrays的copyOf()方法传回的数组是新的数组对象,改变传回数组中的元素值,不会影响原来的数组。copyOf()的第二个自变量指定要建立的新数组长度,如果新数组的长度超过原数组的长度,则保留数组默认值,例如:

    import java.util.Arrays;
    
    public class ArrayDemo {
    public static void main(String[] args) {
        int[] arr1 = {1, 2, 3, 4, 5}; 
        int[] arr2 = Arrays.copyOf(arr1, 5);
        int[] arr3 = Arrays.copyOf(arr1, 10);
        for(int i = 0; i < arr2.length; i++) 
            System.out.print(arr2[i] + " "); 
        System.out.println();
        for(int i = 0; i < arr3.length; i++) 
            System.out.print(arr3[i] + " ");
    }
    } 
    

    执行结果为:

    1 2 3 4 5
    1 2 3 4 5 0 0 0 0 0

    3、add(int index,Eelement):在index位置插入element。将指定元素插入此列表中的指定位置。将当前位于该位置的元素(如果有)和任何后续元素向右移动(向其索引添加一个元素)。

     /**
         * Inserts the specified element at the specified position in this
         * list. Shifts the element currently at that position (if any) and
         * any subsequent elements to the right (adds one to their indices).
         *
         * @param index index at which the specified element is to be inserted
         * @param element element to be inserted
         * @throws IndexOutOfBoundsException {@inheritDoc}
         */
        public void add(int index, E element) {
            rangeCheckForAdd(index);
    
            ensureCapacityInternal(size + 1);  // Increments modCount!!
            System.arraycopy(elementData, index, elementData, index + 1,
                             size - index);
            elementData[index] = element;
            size++;
        }
    

    注:使用System.arraycopy()进行数组复制。

    public static void (Object src,
                                 int srcPos,
                                 Object dest,
                                 int destPos,
                                 int length)
    

    src:源数组;srcPos:源数组要复制的起始位置;dest:目的数组; destPos:目的数组放置的起始位置;length:复制的长度。
    int[] fun ={0,1,2,3,4,5,6};
    System.arraycopy(fun,0,fun,3,3);
    则结果为:{0,1,2,0,1,2,6};

    • 读取:这个方法就比较简单了,ArrayList能够支持随机访问的原因也是很显然的,因为它内部的数据结构是数组,而数组本身就是支持随机访问。该方法首先会判断输入的index值是否越界,然后将数组的 index 位置的元素返回即可。
    public E get(int index) {
        rangeCheck(index);
        return (E) elementData[index];
    }
    private void rangeCheck(int index) {
        if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
    
    • 动态调整数组长度;从上面介绍的向ArrayList中存储元素的代码中,我们看到,每当向数组中添加元素时,都要去检查添加后元素的个数是否会超出当前数组的长度,如果超出,数组将会进行扩容,以满足添加数据的需求。数组扩容有两个方法,==其中开发者可以通过一个 public 的方法ensureCapacity(int minCapacity)来增加ArrayList的容量,而在存储元素等操作过程中,如果遇到容量不足,会调用priavte方法private void ensureCapacityInternal(int minCapacity)==实现。
    public void ensureCapacity(int minCapacity) {
            if (minCapacity > 0)
                ensureCapacityInternal(minCapacity);
        }
        private void ensureCapacityInternal(int minCapacity) {
            modCount++;
            if (minCapacity - elementData.length > 0)
                grow(minCapacity);
        }
        private void grow(int minCapacity) {
            // overflow-conscious code
            int oldCapacity = elementData.length;
            int newCapacity = oldCapacity + (oldCapacity >> 1);
            if (newCapacity - minCapacity < 0)
                newCapacity = minCapacity;
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            // minCapacity is usually close to size, so this is a win:
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    

    从上述代码中可以看出,数组进行扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量的增长大约是其原容量的1.5 倍(从int
    newCapacity = oldCapacity+(oldCapacity>>1)这行代码得出)。这种操作的代价是很高的,因此在实际使用时,我们应该尽量避免数组容量的扩张。当我们可预知要保存的元素的多少时,要在构造 ArrayList 实例时,就指定其容量,以避免数组扩容的发生。

    • 删除:ArrayList 提供了根据下标或者指定对象两种方式的删除功能。需要注意的是该方法的返回值并不相同,如下,根据下标进行删除返回删除的元素,根据指定对象输出返回boolean值:
        public E remove(int index) {
            rangeCheck(index);
    
            modCount++;
            E oldValue = elementData(index);
    
            int numMoved = size - index - 1;
            if (numMoved > 0)
                System.arraycopy(elementData, index+1, elementData, index,
                                 numMoved);
            elementData[--size] = null; // Let gc do its work
            return oldValue;
        }
        public boolean remove(Object o) {
            if (o == null) {
                for (int index = 0; index < size; index++)
                    if (elementData[index] == null) {
                        fastRemove(index);
                        return true;
                    }
            } else {
                for (int index = 0; index < size; index++)
                    if (o.equals(elementData[index])) {
                        fastRemove(index);
                        return true;
                    }
            }
            return false;
        }
    
    • 采用 fail-fast机制
    • 特点:底层基于数组实现,实现随机访问接口,随机访问方便,add(int index,E object)及remove()时都会进行元素的移动,采用system.Arraycopy()方法,且每次扩容采用Arrays.copyof()方法,都产生新的数组。

    Vector(class、数组、随机访问、线程安全)

    LinkedList(class、链表、线程不安全)

    • LinkedList 和 ArrayList一样,都实现了List接口,但其内部的数据结构有本质的不同==。LinkedList是基于链表实现的==(通过名字也能区分开来),所以它的插入和删除操作比ArrayList更加高效。但也是由于其为基于链表的,所以随机访问的效率要比 ArrayList 差。
    public class LinkedList<E>
        extends AbstractSequentialList<E>
        implements List<E>, Deque<E>, Cloneable, java.io.Serializable
    {}
    

    在LinkedList中除了本身自己的方法外,还提供了一些可以使其作为栈、队列或者双端队列的方法。这些方法可能彼此之间只是名字不同,以使得这些名字在特定的环境中显得更加合适。是fail-fast的。

    LinkedList 也是 fail-fast 的

    Stack(class、栈、)

    Map

    HashMap

    • HashMap 是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并允许使用 null值和null键。此类不保证映射的顺序,特别是它不保证该顺序恒久不变。此实现假定哈希函数将元素适当地分布在各桶之间,可为基本操作(get 和 put)提供稳定的性能。
    • HashMap的数据结构:在Java编程语言中,最基本的结构就是两种,一个是数组,另外一个是指针(引用),HashMap就是通过这两个数据结构进行实现。HashMap实际上是一个“链表散列”的数据结构,即数组和链表的结合体。迭代 collection 视图所需的时间与HashMap实例的“容量”(桶的数量)及其大小(键-值映射关系数)成比例。所以,如果迭代性能很重要,则不要将初始容量设置得太高或将加载因子设置得太低。也许大家开始对这段话有一点不太懂,不过不用担心,当你读完这篇文章后,就能深切理解这其中的含义了。
      -构造函数:有三个,分别为

    HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
    HashMap(int initialCapacity):构建一个初始容量为initialCapacity,负载因子为 0.75 的 HashMap。
    HashMap(int initialCapacity, float loadFactor):以指定初始容量、指定的负载因子创建一个 HashMap。

    public HashMap(int initialCapacity, float loadFactor) {
            if (initialCapacity < 0)
                throw new IllegalArgumentException("Illegal initial capacity: " +initialCapacity);
            if (initialCapacity > MAXIMUM_CAPACITY)
                initialCapacity = MAXIMUM_CAPACITY;
            if (loadFactor <= 0 || Float.isNaN(loadFactor))
                throw new IllegalArgumentException("Illegal load factor: " +loadFactor);
            int capacity = 1;
           while (capacity < initialCapacity)
                capacity <<= 1;//保证初始容量是2的n次方。
            this.loadFactor = loadFactor;
            threshold = (int)Math.min(capacity * loadFactor, MAXIMUM_CAPACITY + 1);
            table = new Entry[capacity];
            useAltHashing = sun.misc.VM.isBooted() &&
                    (capacity >= Holder.ALTERNATIVE_HASHING_THRESHOLD);
            init();
    }
    
    • HashMap 底层就是一个数组结构,数组中的每一项又是一个表。当新建一个 HashMap 的时候,就会初始化一个数组。
    static class Entry<K,V> implements Map.Entry<K,V> {
        final K key;
        V value;
        Entry<K,V> next;
        final int hash;
        ……
    }
    

    Entry 是一个static class,其中包含了key和value,也就是键值对,另外还包含了一个 next 的 Entry 指针。我们可以总结出:Entry 就是数组中的元素,每个 Entry 其实就是一个 key-value对,它持有一个指向下一个元素的引用,这就构成了链表。

    • 存储:(根据对象的hash值得到数组中的位置,根据对象的key值得到链表中的位置,如果key已存在,调用equals算法看两个key是否相等,相等则将oldvalue换成newvalue,返回oldvalue)
    /**
         * Associates the specified value with the specified key in this map.
         * If the map previously contained a mapping for the key, the old
         * value is replaced.
         *
         * @param key key with which the specified value is to be associated
         * @param value value to be associated with the specified key
         * @return the previous value associated with <tt>key</tt>, or
         *         <tt>null</tt> if there was no mapping for <tt>key</tt>.
         *         (A <tt>null</tt> return can also indicate that the map
         *         previously associated <tt>null</tt> with <tt>key</tt>.)
         */
    public V put(K key, V value) {
            //其允许存放null的key和null的value,当其key为null时,调用putForNullKey方法,放入到table[0]的这个位置
            if (key == null)
                return putForNullKey(value);
            //通过调用hash方法对key进行哈希,得到哈希之后的数值。该方法实现可以通过看源码,其目的是为了尽可能的让键值对可以分不到不同的桶中
            int hash = hash(key);
            //根据上一步骤中求出的hash得到在数组中是索引i
            int i = indexFor(hash, table.length);
            //如果i处的Entry不为null,则通过其next指针不断遍历e元素的下一个元素。
            for (Entry<K,V> e = table[i]; e != null; e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                    V oldValue = e.value;
                    e.value = value;
                    e.recordAccess(this);
                    return oldValue;
                }
            }
    
            modCount++;
            addEntry(hash, key, value, i);
            return null;
    }
    

    我们看一下方法的标准注释:在注释中首先提到了,当我们 put 的时候,如果 key 存在了,那么新的value会代替旧的value,并且如果key存在的情况下,该方法返回的是旧的 value,如果key不存在,那么返回null。从上面的源代码中可以看出:当我们往 HashMap 中 put 元素的时候,先根据 key 的hashCode重新计算 hash 值,根据 hash值得到这个元素在数组中的位置(即下标),如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。addEntry(hash,key,value,i)方法根据计算出的 hash 值,将 key-value 对放在数组 table 的 i 索引处。
    我们可以看到在 HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。前面说过HashMap的数据结构是数组和链表的结合,所以我们当然希望这个HashMap里面的元素位置尽量的分布均匀些,尽量使得每个位置上的元素数量只有一个,那么当我们用hash算法求得这个位置的时候,马上就可以知道对应位置的元素就是我们要的,而不用再去遍历链表,这样就大大优化了查询的效率。对于任意给定的对象,只要它的 hashCode() 返回值相同,那么程序调用hash(inth)方法所计算得到的hash码值总是相同的。我们首先想到的就是把hash值对数组长度取模运算,这样一来,元素的分布相对来说是比较均匀的。但是,“模”运算的消耗还是比较大的,在HashMap中是这样做的:调用 indexFor(inth,intlength)方法来计算该对象应该保存在table数组的哪个索引处。indexFor(int h, int length) 方法的代码如下:

    /**
         * Returns index for hash code h.
         */
    static int indexFor(int h, int length) {  
        return h & (length-1);
    }
    

    这段代码保证初始化时 HashMap 的容量总是2的n次方,即底层数组的长度总是为 2 的 n 次方。当 length 总是 2 的 n 次方时,h& (length-1)运算等价于对 length 取模,也就是 h%length,但是 & 比 % 具有更高的效 率
    HashMap提高效率的方式:
    (1)长度设成2^n,保证快速得到元素所在数组的下标,有效避免哈希冲突。
    (2)利用红黑树管理链表。
    在jdk1.8版本后,java对HashMap做了改进,在链表长度大于8的时候,将后面的数据存在红黑树中,以加快检索速度,

    LinkedHashMap

    LinkedHashMap和HashMap的区别在于它们的基本数据结构上,看一下LinkedHashMap的基本数据结构,也Entry

    private static class Entry<K,V> extends HashMap.Entry<K,V> {
        // These fields comprise the doubly linked list used for iteration.
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
            super(hash, key, value, next);
        }
    }
    

    列一下Entry里面有的一些属性吧:
    1、K key
    2、V value
    3、Entry<K, V> next
    4、int hash
    5、Entry<K, V> before
    6、Entry<K, V> after

    存储

    LinkedHashMap并未重写父类HashMap的put方法,而是重写了父类HashMap的put方法调用的子方法==void recordAccess(HashMap m) ,void addEntry(int hash, K key, V value, int bucketIndex) 和void createEntry(int hash, K key, V value, int bucketIndex),==提供了自己特有的双向链接列表的实现。

    //这个方法应该挺熟悉的,如果看了HashMap的解析的话
     2 public V put(K key, V value) {
     3     //key为null的情况
     4     if (key == null)
     5         return putForNullKey(value);
     6     //通过key算hash,进而算出在数组中的位置,也就是在第几个桶中
     7     int hash = hash(key.hashCode());
     8     int i = indexFor(hash, table.length);
     9     //查看桶中是否有相同的key值,如果有就直接用新值替换旧值,而不用再创建新的entry了
    10     for (Entry<K,V> e = table[i]; e != null; e = e.next) {
    11         Object k;
    12         if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
    13             V oldValue = e.value;
    14             e.value = value;
    15             e.recordAccess(this);
    16             return oldValue;
    17         }
    18     }
    19 
    20     modCount++;    
    21     //上面度是熟悉的东西,最重要的地方来了,就是这个方法,LinkedHashMap执行到这里,addEntry()方法不会执行HashMap中的方法,
    22     //而是执行自己类中的addEntry方法,
    23     addEntry(hash, key, value, i);
    24     return null;
    25 }
    
    void addEntry(int hash, K key, V value, int bucketIndex) {
     2     //调用create方法,将新元素以双向链表的的形式加入到映射中
     3     createEntry(hash, key, value, bucketIndex);
     4 
     5     // Remove eldest entry if instructed, else grow capacity if appropriate
     6     // 删除最近最少使用元素的策略定义  
     7     Entry<K,V> eldest = header.after;
     8     if (removeEldestEntry(eldest)) {
     9         removeEntryForKey(eldest.key);
    10     } else {
    11         if (size >= threshold)
    12             resize(2 * table.length);
    13     }
    14 }
    
    void createEntry(int hash, K key, V value, int bucketIndex) {
    2     HashMap.Entry<K,V> old = table[bucketIndex];
    3     Entry<K,V> e = new Entry<K,V>(hash, key, value, old);
    4     table[bucketIndex] = e;
    5     //将该节点插入到链表尾部
    6     e.addBefore(header);
    7     size++;
    8 }
    
    private void addBefore(Entry<K,V> existingEntry) {
        after  = existingEntry;
        before = existingEntry.before;
        before.after = this;
        after.before = this;
    }
    

    LinkedHashMap读取元素

    LinkedHashMap重写了父类HashMap的get方法,实际在调用父类getEntry()方法取得查找的元素后,再判断当排序模式accessOrder为true时(即按访问顺序排序),先将当前节点从链表中移除,然后再将当前节点插入到链表尾部。由于的链表的增加、删除操作是常量级的,故并不会带来性能的损失。
    https://www.cnblogs.com/xiaoxi/p/6170590.html

    展开全文
  • 集合知识点总结1

    千次阅读 2017-11-20 22:25:12
    集合就是一个已经封装好的对象 集合三大接口 Collection 所有集合类的跟接口 Map 映射接口,存放键值对 Iterator 遍历集合的迭代接口 ...Java的集合框架是由很多...Collection(单列集合的跟接口)(泛型)1.2JDK版本

    集合就是一个已经封装好的对象

    集合三大接口

    Collection 所有集合类的跟接口

    Map 映射接口,存放键值对

    Iterator 遍历集合的迭代接口

    Java的集合框架是由很多接口、抽象类、具体类组成的,都位于java.util包中。除Comparable位于Java.lang包


    Collection单列集合的跟接口)<E>(泛型)1.2JDK版本

    collection因为是接口不能实例化不能新建对象,所以要用实现类,那么要父类引用指向子类对象。

    list有序(存和取的顺序一致),有索引可以储存重复

    ArrayList-前缀告诉底层是数组,LinkedList-前缀告诉底层是链表实现的,Vector-前缀底层也是数组实现的,但是不遵循list命名。Vector出现比ArrayList要早,在JDK1.0版本的时候出现的,ArrayList则在1.2版本的时候出现的。

    @SupperWarning(“rawtypes”,"unchecked")//保持类型,不检查,但没有加泛型,有一定的安全隐患

    Collection a=new ArrayList();//父类引用指向子类对象
    boolean b= a.add("abc");
    boolean c=a.add(true);//自动装箱new boolean(true);
    boolean d=a.add(100);
    boolean e=a.add(new Client(“李四”,23));//将Client类的封装;
    boolean f=a.add("abc")//能放进来重复的元素;
    System.out.println(c);//==System.out.println(c.toString);实质上重写了,要往上父类、爷爷类查找。

    return true;

    return true;

    return true;

    return true;

    return true;

    [abc,true,100,Client[name=李四,age=23],abc]

    如果是List集合一直返回true,因为可以存储重复元素。如果是Set集合当储存重复元素时候,就会返回false。


    set无序(存和取的顺序不一致),无索引不可以储存重复,Set集合由Set接口和Set接口的实现类组成。Set接口继承了Collection接口,因此包含Collection接口的所有方法。Set接口中的方法和Collection接口一致。 

    HashSet-底层哈希算法 TreeSet-底层二叉树算法

    Set接口的常见实现类: 
    HashSet:内部数据结构是哈希表,是不同步的。 
    HashSet如何保证该集合中元素的唯一性: 
    是通过对象的hashCode和equals方法啊来完成对象的唯一性的。如果对象的hashCode值不同,那么不用判断equals方法,就直接存储到哈希表中。如果对象的hashCode值相同,那么要再次判断对象的equals方法是否为true。如果为true,视为相同元素,不存。如果为false。那么视为不同元素,就进行存储。 

    如果元素存储到HashSet集合中,必须覆盖hashCode方法和equals方法。一般情况下,如果定义的类会产生很多对象,比如人,学生,书,通常都需要覆盖equals方法和hashCode方法,建立对象判断是否相同的依据。 
    HashSet是无序的,但是下面的子接口LinkedHashSet是具有可预知迭代顺序的set接口的哈希表和链表实现的,有序。 
    TreeSet:可以对Set集合中的元素进行指定顺序的排序。是不同步的。 
    TreeSet如何保证该集合中元素的唯一性: 
    就是根据比较方法compareTo的返回结果是否是0,是0,就判定为相同元素,不存。

    TreeSet对元素进行排序的方式一: 
    让元素自身具备比较功能,就需要实现Comparable接口。覆盖CompareTo方法。 
    如果不要按照对象中具备的自然顺序进行排序。如果对象中不具备自然顺序。这个时候可以使用TreeSet集合的第二种排序方式。 
    TreeSet集合的第二种排序方式二: 
    让集合自身具备比较功能,定义一个类实现Comparator接口。覆盖compare方法。将该类对象作为参数传递给TreeSet集合的构造函数。相对来说,这个方法比较常见。

    HashSet例一:往hashSet集合中存储Person对象,如果姓名和年龄相同,视为同一个人(相同元素)。 
    先新建一类Person:

    public class Person {  
    
        private String name;  
        private int age;  
        public Person() {  
            super();  
    
        }  
        public Person(String name, int age) {  
            super();  
            this.name = name;  
            this.age = age;  
        }  
    
        /** 
         * 覆盖Object类中的hashCode方法。建立Person对象自己特点的哈希值算法。 
         */  
        public int hashCode(){  
            //System.out.println(this+"...hascode...");
            return name.hashCode() + age*27;//乘以27是为了尽量保证哈希值唯一。  
        }  
        /**  
         * 覆盖Object类中的equals方法,建立Person对象判断是否相同的依据。 根据Person自身的特点来判断。  
         */  
        public boolean equals(Object obj){  
            //System.out.println(this+"...equals..."+obj);  
            if(!(obj instanceof Person))  
                return false;  
    
            Person p = (Person)obj;  
    
            return this.name.equals(p.name) && this.age == p.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;  
        }  
    
        @Override  
        public String toString() {  
    
            return name+":"+age;  
        }  

    再新建HashSetTest类,进行存储与取出Person对象的操作:

    /*
    往hashSet集合中存储Person对象,如果姓名和年龄相同,视为同一个人(相同元素)。
     * */
    public class HashSetTest {
    
        public static void main(String[] args) {
            HashSet hs=new HashSet();
            /*HashSet集合数据结构是哈希表,所以存储元素的时候,使用的元素的hashCode方法来确定位置,如果位置相同,再通过元素的equals来确定是否相同。*/
            hs.add(new Person("abc1",21));
            hs.add(new Person("abc2",22));
            hs.add(new Person("abc3",23));
            hs.add(new Person("abc4",24));
            hs.add(new Person("abc2",22));
    
            Iterator it=hs.iterator();
            while(it.hasNext()){
                Person p=(Person)it.next();//因为存入集合容器中的元素都会向上转型为Object类,会丢失掉Person类的特有属性,所以必须强制向下转型为Person类,才可以拿到它的name和age属性;另外,由于同时要拿两个属性,所以等于说it.next()取到的对象不止使用一次,必须用一个变量来接收。
                System.out.println(p.getName()+":"+p.getAge());
            }
        }
    }

    结果输出为: 
    abc3:23 
    abc2:22 
    abc4:24 
    abc1:21

    Java集合关系图:


    展开全文
  • 数组与集合的区别:--博客园:师妹开讲啦 Java中的数组是存放同一类型数据的集合。数组可以存放基本数据类型的数据,也可以存放引用数据类型的数据,但数组的长度是固定的。 集合只能存放引用数据类型的数据,不能...

    数组与集合的区别:--博客园:师妹开讲啦

    Java中的数组是存放同一类型数据的集合。数组可以存放基本数据类型的数据,也可以存放引用数据类型的数据,但数组的长度是固定的。

    集合只能存放引用数据类型的数据,不能存放基本数据类型的数据,但集合的数目是可变的。

     

    所有的集合类都存放在java.util包中

     List接口是元素有序,可重复的集合,添加自定义类时需要重写equals()方法

    List的特有方法:

    添加时会出现异常

    正确的添加方法

    ArrayList集合中,插入一个元素时,列表需要将插入点后面的所有元素向后移动,而在删除(remove())一个元素时,需要将被删除元素后面的所有元素向前移动

    ArrayList集合允许所有的元素,包括null

    例1:ArrayList的一些基本方法

    public class ArrayListText{

      public static void main(String[] args){

        Collection<String> collection=new ArrayList<String>();

        List<String> list=new ArrayList<String>();

        collection.add("1");

        collection.add("2");

        collection.add("3");

        list.add("A");

        list.add("B");

        list.add("C");

        list.addAll(0,collection);//向指定位置添加一个集合的所有元素

        System.out.println("list集合:"+list);

        List<String> subList=list.subList(1,5);

        System.out.println("subList集合:"+subList);

        System.out.println("设置list集合元素前::"+list.get(3));

        list.set(3,"Set");

        System.out.println("设置list集合元素后::"+list.get(3));

        System.out.println("list集合中,元素3的位置:"+list.indexOf("3")+",元素D的位置:"+list.indexOf("D"));//获取指定元素在集合中的索引位置,若不存在则返回-1

        String arrString[]=list.toArray(new String[]{});//将集合转为字符串数组

        for(String str:arrString){

          System.out.print(str+" ");

        }

        System.out.println("\nlist集合是否为空:"+list.isEmpty());

        list.clear();

        System.out.println("\nlist集合是否为空:"+list.isEmpty());

      }

    }

    结果:

    list集合:[1,2,3,A,B,C]

    subList集合:[2,3,A,B]

    设置list集合元素前::A

    设置list集合元素后::Set

    list集合中,元素3的位置:2,元素D的位置:-1

    1  2  3  Set  B  C

    list集合是否为空:false

    list集合是否为空:true

    将数组转换为ArrayList集合

    String[] str={"W","E","C","O","M","E"};

    List<String> list=Arrays.asList(str);

    ArrayList<String> arrList=new ArrayList<String>();

    arrList.addAll(list);

    去除ArrayList集合中的重复元素

    List集合判断元素是否相同,依据的是元素的equals方法。

    ArrayList集合中Contains()remove()底层用的都是equals()方法--博客园:师妹开讲啦

    LinkedList是链表类,链表结构的优点是便于向集合插入和删除元素。因为在插入或删除元素时,不需要移动任何元素,LinkedList也可以存在null元素

    获取表头的方法:

    getFirst():仅仅是获取表头     element():获取但不移除此链表头   peek():获取但不移除此链表头    poll():获取并移除此链表头  pop():获取并移除此链表头

    例2:LinkedList获取链表头

    public class LinkedListText{

      public static void main(String[] args){

        LinkedList<String> link=new LinkedList<String>();

        link.add("1");

        link.add("2");

        link.add("3");

        link.addFirst("F");

        link.addLast("L");

        LinkedList<String> newLink=new LinkedList<String>(link);

        System.out.println("添加元素后:\n\t"+link);

        System.out.println("get()方法获取表头:"+link.getFirst());

        System.out.println("使用get()方法后:\n\t"+link);

        System.out.println("element()方法获取表头:"+link.element());

        System.out.println("使用element()方法后:\n\t"+link);

        System.out.println("peek()方法获取表头:"+link.peek());

        System.out.println("使用peek()方法后:\n\t"+link);

        System.out.println("poll()方法获取表头:"+link.poll());

        System.out.println("使用poll()方法后:\n\t"+link);

        System.out.println("pop()方法获取表头:"+link.pop());

        System.out.println("使用pop()方法后:\n\t"+link);

        System.out.println("******使用链表的先进先出*******");

        int  len=newLink.size();

        for(int i=0;i<len;i++){

          System.out.print(newLink.poll()+" ");

        }

      }

    }

    结果:

    添加元素后:

    [F,1,2,3,L]

    get()方法获取表头:F

    使用get()方法后:

    [F,1,2,3,L]

    element()方法获取表头:F

    使用element()方法后:

    [F,1,2,3,L]

    peek()方法获取表头:F

    使用peek()方法后:

    [F,1,2,3,L]

    poll()方法获取表头:F

    使用poll()方法后:

    [1,2,3,L]

    pop()方法获取表头:1

    使用pop()方法后:

    [2,3,L]

    *****使用链表的先进先出*****

    F 1 2 3 L

    先进先出

    myGet()方法改为return link.removeFirst();则变为先进后出

     Set集合是元素无序,不可重复的集合,允许包含null元素,添加自定义类一定要重写equals()和hashcode()方法

    HashSet类可以快速地定位一个元素,但放到HashSet集合中的元素需要重写equals()和hashcode()方法,而TreeSet集合将放进其中的元素按序存放

    HashSet是按哈希算法来存放集合中的元素的,使用哈希算法可以提高存取的效率

    HashSet集合的特点:1.不能保证元素的排列顺序,集合中元素的顺序随时有可能发生改变  2.集合中最多允许存在一个null元素  3.HashSet集合不是线程同步的

    LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能,LinkedHashSet()使用链表维护了一个添加进集合中的顺序,导致当我们遍历LinkedHashSet集合元素时是按添加进去的顺序遍历的,但不能说明它是有序的!

    TreeSet类实现了java.util包中的Set接口和SortedSet接口,TreeSet集合中的元素在默认情况下是升序排序

    TreeSet集合的特点:1.向TreeSet中添加的元素必须是同一个类的  2.从小到大遍历   3.自定义类没有实现Comparable接口时,当向TreeSet中添加此对象时,报错

    TreeSet的排序方法:

    方法1:自定义类实现Comparable接口

    class Person implements Comparable{

      private String name;

      private String age;

      public Person(){

      }

      public Person(String name,int age){

        this.name=name;

        this.age=age;

      }

      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);

        }

      }

      public String toString(){

        return ("姓名:"+name+",年龄:"+age+"\n");

      }

    }

    public class TreeSetDemo{

      public static void main(String[] args){

        Set<Person> tset=new TreeSet<Person>();

        tset.add(new Person("小强",21));

        tset.add(new Person("小伟",23));

        tset.add(new Person("小强",21));

        tset.add(new Person("小强",21));

        tset.add(new Person("小琴",20));

        tset.add(new Person("小婷",20));

        System.out.println(tset);

      }

    }

    结果:

    [姓名:小婷,年龄:20

    ,姓名:小琴,年龄:20

    ,姓名:小强,年龄:21

    ,姓名:小伟,年龄:23

    ]

    如果这样重写comepareTo方法,则加入TreeSet的顺序是怎样的,输出时也是怎样的

    方法2:

    泛型的本质是参数化类型,泛型类型标识只能是引用数据类型。--博客园:师妹开讲啦

    在创建泛型类的对象时,最好指定该对象的数据类型,如果不指定,就会出现不安全的警告信息,但不影响程序的运行。因为在没有指定类型时,系统会默认以Object类型接收,也就是在定义时将泛型擦拭。

    Java规定不能使用泛型类型的数组,如:Generics<String> genArr[]=new Generics<String>[10];因为在Java代码编译时,会有擦拭处理,这时就会变为Object obj[]=genArr。这是一个Object类型的数组,试图向其插入其他类型的元素时,编译器会抛出ArrayStoreException异常

    按长度排序:

    错误原因:Cannot make a static reference to the non-static type T

    正确使用方法:

    通配符<?>和泛型<T>的区别:

    使用无界通配符“?”设置对象时,不能通过setter()方法设置被泛型指定的内容,但可以设置为null。

     

    在设置类型形参的上限时,无论使用的是类还是接口作为类型的形参的上限都是使用extends关键字限制。一个类型参数可以用多个限界类型。限界类之间用“&”分隔开。在多个限界类型中,可以有多个接口,但只能有一个类。设置下限使用super

    不可以将类声明为:class Generics<? extends Number>{},因为通配符是用来声明一个泛型类的变量的,而不是用来创建一个泛型类的。

    泛型类也可以作为父类或子类。当一个类的父类是泛型类时,该类也必定是泛型类

    泛型类可以以非泛型类做父类,这时不再需要传递参数给父类,所有的类型参数都是为自己准备的

    泛型类和非泛型类在继承时的主要差别是:泛型类的子类必须将泛型父类所需要的类型参数沿着继承链向上传递

    如果泛型接口声明为:interface InterGenerics<T extends Serializable>,则实现类必须声明为:class Generics<T extends Serializable> implements InterGenerics<T >,若声明为:class Generics<T extends Serializable> implements InterGenerics<T extends Serializable>,则编译无法通过

    泛型方法可以写在泛型类或泛型接口中,也可以写在普通类或普通接口中。在泛型类或泛型接口中的方法,本质上都是泛型方法,但在实际中很少在泛型类或泛型接口中显示定义泛型方法

     

    集合的输出:1.Iterator:迭代器输出(提供遍历各种集合类型的迭代器)   2.foreach:JDK1.5新增的输出

    迭代删除:

    Iterator<String> it=link.iterator();

    while(it.hasNext){

      it.next();

      it.remove();

    }

    注释:在调用remove()方法前需要使用next()方法指定要删除的元素,否则运行时将产生IllegalStateException异常

     Map接口提供的是key-value的映射,其中key不允许重复,每一个key只能映射一个value,常用String类作为Map的键

    一个key-value对是一个Entry,所有的Entry是用Set存放的,不可重复。key是用Set来存放的,不可重复,value是用Collection存放的,可重复

    Map.Entry接口是静态的,所以可以直接使用”外部类.内部类“的形式调用

    HashMap类是实现Map集合,对于元素的添加和删除有着较高的效率。HashMap类提供所有可选的映射操作,并允许使用null值和null键,但必须保证键是唯一的,HashMap是非同步的,也不保证映射顺序--博客园:师妹开讲啦

    结果:null

          Zhangsan1

    例3:获取Map集合中的全部key和value

    public class HashMapDemo{

      public static void main(String[] arg){

        Map<Integer,String> map=new HashMap<Integet,String>();

        map.put(1,"清华大学");

        map.put(2,"北京大学");

        map.put(3,"复旦大学");

        map.put(4,"武汉大学");

        map.put(5,"中国科技大学");

        map.put(6,"中国矿业大学");

        Set<Integer> set=map.keySet();

        Iterator<Integet> itKey=set.iterator();

        System.out.println("Map集合中全部的key:");

        while(itKey.hasNext()){

          System.out.print(itKey.next()+" ");

        }

        System.out.println();

        Collection<String> c=map.values();

        Iterator<String> itValue=c.iterator();

        System.out.println("Map集合中全部的value:");

        while(itValue.hasNext()){

          System.out.print(itValue.next()+" ");

        }

      }

    }

    结果:

    Map集合中全部的key:

    1 2 3 4 5 6

    Map集合中全部的value:

    清华大学 北京大学 复旦大学 武汉大学 中国科技大学 中国矿工大学

    另一种遍历效果:

    Set<String> keySet=map.keySet();

    Iterator<String> it=keySet.itetator();

    while(it.hasNext()){

    String key=it.next();

    String value=map.get(key);

    System.out.println(key+"="+value);

    }

    例4:使用Iterator输出Map集合

    public class hashMapDemo{

      public static void main(String[] args){

        Map<Integer,String> map=new HashMap<Integet,String>();

        map.put(1,"清华大学");

        map.put(2,"北京大学");

        map.put(3,"复旦大学");

        map.put(4,"武汉大学");

        map.put(5,"中国科技大学");

        map.put(6,"中国矿业大学");

        Set<Map.Entry<Integer,String>> set=map.entrySet();

        Iterator<Map.Entry<Integet,String>> it=set.iterator();

        System.out.println("Key--------Value");

        while(it.hasNext()){

          Map.Entry<Integer,String> mapEntry=it.next();

          System.out.println(""+mapEntry.getKey()+"-------"+mapEntry.getValue());

        }

      }

    }

    结果:

    key------value

    1-----清华大学

    2-----北京大学

    3-----复旦大学

    4-----武汉大学

    5-----中国科技大学

    6-----中国矿业大学

    使用foreach输出:

    for(Map.Entry<Integet,String> mapEntry:map.entryKey()){

      System.out.println(""+mapEntry.getKey()+"-------"+mapEntry.getValue());

    }

     

    TreeMap类不但实现了Map接口,还实现了SortedMap接口,因此,TreeMap集合中的映射关系具有一定的顺序性。与HashMap相比,TreeMap集合对元素的添加、删除和定位映射性能较低

    Collections类可以对集合的元素进行排序、反序、去极值、循环移位、查询和修改等功能,还可以将集合对象设置不可变、对集合对象实现同步控制等方法。

    Collections的排序方法:

     

    结果:-2   //-(插入点)-1

     

    自定义一个二分法查询

     

     

    Collections类所提供的方法均为静态方法,可以直接通过“类名.方法()"的形式调用

    Collections常用方法:addAll(Collection c,T...elements)--Collections.addAll(list,"1","2","3")  binarySearch(List list,T key)-使用此方法前必须先使用sort(List list,Comparator c)方法进行排序  copy(List dest,List src)//将src集合中的所有元素复制到dest集合中  fill(List list,T obj)//使用指定元素替换指定集合中的所有元素  max(Collection coll) max(Collection coll,Comparator c)  replaceAll(List list,T oldVal,T newVal)//使用另一个元素替代集合中指定的所有元素  reverse(List list) 

    Stack-栈是一种特殊的线性表,它仅限于在表尾进行元素的添加与删除操作。栈的表尾成为栈顶,表头成为栈底。栈是采用“先进后出”的数据存储方式,若栈中没有任何元素,则成为空栈。栈是Vector的子类

    empty()//判断该栈是否为空,若为空返回true  peek()//获取栈顶元素,但不删除它  pop()//获取栈顶元素,并删除它  push()//将元素添加到栈顶中  search()、、查找指定元素在栈中位置,起始位置为1,不是0

    Hashtable类是Map的子类,是JDK1.0是出现的,Hashtable类是同步的,线程安全的,不允许存放null键和null值,其他功能与HashMap类似

     

    可变参数(String...str)在使用时一定要定义在参数列表的最后面。String str,int...i

    //报错   原因类继承Object,打印的时候想打印Arrays.toStringarr,但若此处省略Arrays则默认为调用ObjecttoString()方法。

     

    转载于:https://www.cnblogs.com/xiaoshimei/p/6227603.html

    展开全文
  • 集合知识点学习总结

    2012-04-03 21:01:50
    1. 所有Java集合类都位于java.util包中,与Java数组不同,Java集合中不能存放基本数据类型,只能存放对象引用。 2. Set、List、Map统称为Java集合。 3. 在将对象存储到集合类中时,为加快存储速度,要求被在
  • 说明:先从整体介绍了Java集合框架包含接口和类,然后总结集合框架中一些基本知识和关键,并结合实例进行简单分析。 1、综述 所有集合类都位于java.util包下。集合中只能保存对象(保存对象引用变量)。...
  • 说明:先从整体介绍了Java集合框架包含接口和类,然后总结集合框架中一些基本知识和关键,并结合实例进行简单分析。   1、综述  所有集合类都位于java.util包下。集合中只能保存对象(保存对象引用...
  • 说明:先从整体介绍了Java集合框架包含接口和类,然后总结集合框架中一些基本知识和关键,并结合实例进行简单分析。   1、综述  所有集合类都位于java.util包下。集合中只能保存对象(保存对象引用...
  • Collection是最基本的集合接口,一个Collection代表一组Object,即Collection元素(Elements)。Java JDK不提供直接继承自Collection类,Java JDK提供类都是继承自Collection“子接口”如List和Set 所有实现...
  • Java集合 collection接口主要继承接口和主要实现类 List(有序Collection) ArrayList 排列有序可重复 底层使用数组 查找快,增删慢,因为不能有间隔,所有增删都需要移动数据 线程不安全 当容量不够时候,...
  • 集合完全由其元素决定,A=B⟺ ∀x(x∈A⟷x∈B),因此要证A=B只需证A⊆且B⊆A 形如{x | P(x)}也未必是集合,例如罗素悖论R={x | x∉R},若R为集合则R∈R...幂集P(A):A的所有子集(包括∅和A自身)所组成的集合。 ...
  • InputStream : 是所有字节输入流超类,一般使用它子类:FileInputStream等,它能输出字节流; InputStreamReader : 是字节流与字符流之间桥梁,能将字节流输出为字符流,并且能为字节流指定字符集,可输出...

空空如也

空空如也

1 2 3 4 5 ... 16
收藏数 310
精华内容 124
关键字:

集合的所有知识点总结