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

    一、描述Java集合框架

      集合,在Java语言中,将一系类的对象看成一个整体。

      首先查看jdk中的Collection类的源码后会发现Collection是一个接口类,其继承了java迭代接口Iterable.

    API:

    java集合框架图:

      在图当中,虚线框的类型是接口,短横线框的类型是抽象类,实线框的类型是实现类。

    集合的包在java.util下

      集合框架的顶层接口:java.util.Collection,迭代器接口:Java.util.Iterator;

      Collection接口有两个主要的字接口List和Set,但是要注意Map不是Collection的子接口,因为其本身就是一个顶层接口,放在此是由于映射键与值使用了set。Collection中可以储存的元素间无序可以重复组各自独立的元素,即其内的每个位置仅持有一个元素,同时允许有多个null元素对象。

      Collection接口中的方法如下:

    集合分类有三种List 、Set、Map

    1、列表:java.util.List   

      List中储存的元素实现类排序,而且可以重复储存相关元素。List有ArrayList、Vector、LinkedList等等的实现类,ArrayList和Vector都是数组的实现,Vector线程是安全的,LinkedList是链表的实现。

      List接口有两个常用的实现类ArrayList和LinkedList。

    (一)ArrayList:

      ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。

          ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。可以这样理解ArrayList就是基于数组的一个线性表,只不过数组的长度可以动态改变而已。

    编程练习ArrayList的使用,示例:

    1、

     1 import java.util.ArrayList;
     2 import java.util.List;
     3 
     4 public class ArrayListDemo {
     5 
     6     public static void main(String[] args) {
     7         //实例化一个集合实例,下面两个集合实例化都是可以的
     8         List list0 = new ArrayList();
     9         ArrayList list1 = new ArrayList();
    10         //向列表中添加元素
    11         list0.add(123456);
    12         list0.add(null);
    13         list0.add("abcdef");
    14         //显示指定位置的元素
    15         System.out.println(list0.get(1));//索引是从0开始的,结果显示null
    16         //显示列表中所有的元素
    17         for(Object l : list0) {
    18             System.out.println(l);
    19         }

    2、

     1 //在列表集合ArrayList中查找一个元素,显示“找到”与“未找到”
     2         ArrayList list2 = new ArrayList();
     3         list1.add(123);
     4         list1.add(null);
     5         list1.add("abc");
     6         if(list2.contains(123)) {
     7             System.out.println("元素存在");
     8         }
     9         else {
    10             System.out.println("元素不存在");//结果:元素存在
    11         }

     3、来一个Vector使用的例子

     1 package org.sl.jiheDemo;
     2 
     3 import java.util.Vector;
     4 
     5 public class VectorDemo {
     6 
     7     public static void main(String[] args) {
     8         Vector v = new Vector();
     9         v.add("小小酥");
    10         v.add(123);
    11         v.add(null);
    12         for(Object a : v) {
    13             System.out.println(a);
    14         }
    15     }
    17 }

    ArrayList面试小问答:

    a、如果在初始化ArrayList的时候没有指定初始化长度的话,默认的长度为10. 

    b、ArrayList在增加新元素的时候如果超过了原始的容量的话,ArrayList扩容ensureCapacity的方案为“ 原始容量*3/2+1 "哦

    c、ArrayList是线程不安全的,在多线程的情况下不要使用。

    如果一定在多线程使用List的,您可以使用Vector,因为Vector和ArrayList基本一致,区别在于Vector中的绝大部分方法都使用了同步关键字修饰,这样在多线程的情况下不会出现并发错误哦,还有就是它们的扩容方案不同,ArrayList是通过“原始容量*3/2+1”,而Vector是允许设置默认的增长长度,Vector的默认扩容方式为原来的2倍。

     切记Vector是ArrayList的多线程的一个替代品。

    d、ArrayList实现遍历的几种方法,三种,示例:

     1 import java.util.ArrayList;
     2 import java.util.Iterator;
     3 import java.util.List;
     4 
     5 public class ListDemo {
     6 
     7     public static void main(String[] args) {/*
     8          * ArrayList实现遍历有哪几种方法
     9          */
    10         ArrayList<String> list = new ArrayList<String>();
    11         list.add("abc");
    12         list.add("小小酥");
    13         list.add("def");
    14         //第一种方法是使用foreach遍历List
    15         for(String str : list) {         //这里也可以用for(int i=0; i<list.size(); i++)
    16             System.out.println(str);
    17         }
    18         //第二种方法把链表变为数组相关的内容进行遍历
    19         String[] strArray = new String[list.size()];
    20         list.toArray(strArray);
    21         for(String str1 : strArray) {     //也可以用for(int i=0; i<strArray.length; i++)
    22             System.out.println(str1);
    23         }
    24         //第三种方法 使用迭代器进行相关遍历
    25             //首先获取迭代器对象,Iterator
    26         Iterator<String> ite = list.iterator();
    27         while(ite.hasNext()) {                    //hasNext():表示是否有下一个元素
    28             System.out.println(ite.next());     //next():指向下一个元素
    29         }
    30     }
    31 
    32 }

    显示结果:我截图就行了,三种方法显示了三次结果,结果都是一样的。

    (二)LinkedList

      LinkedList的链式线性表的特点为: 适合于在链表中间需要频繁进行插入和删除操作。

      LinkedList的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。速度你懂的。

       可以这样理解LinkedList就是一种双向循环链表的链式线性表,只不过存储的结构使用的是链式表而已。

       对于LinkedList的详细使用信息以及创建的过程可以查看jdk中LinkedList的源码,这里不做过多的讲解。

    LinkedList面试小问答:

     a.LinkedList和ArrayList的区别和联系

        ArrayList数组线性表的特点为:类似数组的形式进行存储,因此它的随机访问速度极快。

       ArrayList数组线性表的缺点为:不适合于在线性表中间需要频繁进行插入和删除操作。因为每次插入和删除都需要移动数组中的元素。

       LinkedList的链式线性表的特点为: 适合于在链表中间需要频繁进行插入和删除操作。

       LinkedList的链式线性表的缺点为: 随机访问速度较慢。查找一个元素需要从头开始一个一个的找。速度你懂的。

    b.LinkedList的内部实现

             对于这个问题,你最好看一下jdk中LinkedList的源码。这样你会醍醐灌顶的。

             这里我大致说一下:

             LinkedList的内部是基于双向循环链表的结构来实现的。在LinkedList中有一个类似于c语言中结构体的Entry内部类。

             在Entry的内部类中包含了前一个元素的地址引用和后一个元素的地址引用类似于c语言中指针。  

    c.LinkedList不是线程安全的

             注意LinkedList和ArrayList一样也不是线程安全的,如果在对线程下面访问可以自己重写LinkedList

             然后在需要同步的方法上面加上同步关键字synchronized

    d.LinkedList的遍历方法:示例

     1 import java.util.LinkedList;
     2 import java.util.List;
     3 
     4 public class LinkedListDemo {
     5         public static void main(String[] args) {
     6            List<String> list = new LinkedList<String>();
     7            list.add("小小酥");
     8            list.add("小张");
     9            list.add("abc");
    10            //使用foreach遍历LinkedList
    11            for(String str : list) {
    12                System.out.println(str);
    13            }
    14            //使用数组遍历
    15            String[] strArray = new String[list.size()];
    16            list.toArray(strArray);
    17            for(String str1 : strArray) {
    18                System.out.println(str1);
    19            }
    20            //还有其他的方法的话我没去研究了
    21     }
    22 }

    结果如下:

    e.LinkedList可以被当做堆栈来使用

            由于LinkedList实现了接口Dueue,所以LinkedList可以被当做堆栈来使用,这个你自己研究吧。

    2、集:java.util.Set     

    Set接口也是Collection接口的一个常用的子接口 ,Set中的元素实现了不重复,无序,不允许有重复的元素,最多允许一个null元素对象。

    查看Set接口的源码:

    这里就自然而然的知道Set接口是Collection接口的子接口了吧。

    需要注意的是:虽然Set中元素没有顺序,但是元素在set中的位置是由该元素的HashCode决定的,其具体位置其实是固定的。

    此外需要说明一点,在set接口中的不重复是由特殊要求的。

        举一个例子:对象A和对象B,本来是不同的两个对象,正常情况下它们是能够放入到Set里面的,但是

        如果对象A和B的都重写了hashcode和equals方法,并且重写后的hashcode和equals方法是相同的话。那么A和B是不能同时放入到Set集合中去的,也就是Set集合中的去重和hashcode与equals方法直接相关。

    示例:截图(set集是不保证顺序的,没有索引的说明)

    Set接口的常见实现类有HashSet,LinedHashSet和TreeSet这三个。下面我们将分别讲解这三个类:

    (1)HashSet

             HashSet是Set接口的最常见的实现类了。其底层是基于Hash算法进行存储相关元素的。

     下面是HashSet的部分源码::

    对于HashSet的底层就是基于HashMap来实现的。

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

    a.HashSet中存放null值

            HashSet中时允许出入null值的,但是在HashSet中仅仅能够存入一个null值哦。

     b.HashSet中存储元素的位置是固定的

            HashSet中存储的元素的是无序的,这个没什么好说的,但是由于HashSet底层是基于Hash算法实现的,使用了hashcode,

            所以HashSet中相应的元素的位置是固定的

     c.遍历HashSet的几种方法:

     1 import java.util.HashSet;
     2 import java.util.Iterator;
     3 import java.util.Set;
     4 
     5 public class SetDemo {
     6 
     7     public static void main(String[] args) {
     8         Set<String> set=new HashSet<String>();
     9         //向集合中放入元素,set集是不保证顺序的,没有索引的说明
    10          set.add("Hello");
    11          set.add("world");
    12          set.add("小小酥");
    13          //第一种set集合直接遍历
    14          for(String str : set) {
    15              System.out.println(str);
    16          }
    17          //第二种使用数组方法
    18          String[] strArray = new String[set.size()];
    19          set.toArray(strArray);
    20          for(String str1 : set) {
    21              System.out.println(str1);
    22          }
    23         //第三种使用迭代器的方法,Iterator     
    24          Iterator<String> ite = set.iterator();
    25          while(ite.hasNext()) {
    26              System.out.println(ite.next());
    27          }
    28          System.out.println("集合的元素个数为:"+set.size());
    29          System.out.println("集合中的元素为:"+set.toString());
    30     }
    31 }

    结果输出:

    (2)LinkHashSet

           LinkHashSet不仅是Set接口的子接口而且还是上面HashSet接口的子接口。

           查看LinkedHashSet的部分源码如下:

     

    3)TreeSet

           TreeSet是一种排序二叉树。存入Set集合中的值,会按照值的大小进行相关的排序操作。底层算法是基于红黑树来实现的。

           TreeSet和HashSet的主要区别在于TreeSet中的元素会按照相关的值进行排序~

           TreeSet和HashSet的区别和联系

           1. HashSet是通过HashMap实现的,TreeSet是通过TreeMap实现的,只不过Set用的只是Map的key
           2. Map的key和Set都有一个共同的特性就是集合的唯一性.TreeMap更是多了一个排序的功能.
           3. hashCode和equal()是HashMap用的, 因为无需排序所以只需要关注定位和唯一性即可.
               a. hashCode是用来计算hash值的,hash值是用来确定hash表索引的.
               b. hash表中的一个索引处存放的是一张链表, 所以还要通过equal方法循环比较链上的每一个对象
                  才可以真正定位到键值对应的Entry.
               c. put时,如果hash表中没定位到,就在链表前加一个Entry,如果定位到了,则更换Entry中的value,并返回旧value
           4. 由于TreeMap需要排序,所以需要一个Comparator为键值进行大小比较.当然也是用Comparator定位的.
               a. Comparator可以在创建TreeMap时指定
               b. 如果创建时没有确定,那么就会使用key.compareTo()方法,这就要求key必须实现Comparable接口.
               c. TreeMap是使用Tree数据结构实现的,所以使用compare接口就可以完成定位了.

     

    3、映射:java.util.Map

     Map接口实现的是一组Key-Value的键值对的组合。 Map中的每个成员方法由一个关键字(key)和一个值(value)构成。Map接口不直接继承于Collection接口(需要注意啦),因为它包装的是一组成对的“键-值”对象的集合,而且在Map接口的集合中也不能有重复的key出现,因为每个键只能与一个成员元素相对应。如图

    (图简略将就看看)

    在我们的日常的开发项目中,我们无时无刻不在使用者Map接口及其实现类。Map有两种比较常用的实现:HashMap和TreeMap等。HashMap也用到了哈希码的算法,以便快速查找一个键,TreeMap则是对键按序存放,因此它便有一些扩展的方法,比如firstKey(),lastKey()等,你还可以从TreeMap中指定一个范围以取得其子Map。键和值的关联很简单,用pub(Object key,Object value)方法即可将一个键与一个值对象相关联。用get(Object key)可得到与此key对象所对应的值对象。

    简单介绍一下HashMap和Hashtable

    HashMap

      HashMap实现了Map、CloneMap、Serializable三个接口,并且继承自AbstractMap类。HashMap基于hash数组实现,若key的hash值相同则使用链表方式进行保存。

      新建一个HashMap时,默认的话会初始化一个大小为16,负载因子为0.75的空的HashMap

    先来个示例,我懒得敲了,直接截图

    HashMap与Hashtable的区别:

    HashMap:线程不安全,可以在列表中放置一个key为null的元素,也可以多个value为null的元素

    Hashtable:线程安全,不允许键和值有null的元素。

    还有一个集合工具排序那个,见下章,对了不对的请指教,谢谢

  • 相关阅读:
    open_basedir restriction in effect,解决php引入文件权限问题 解决方法
    Linux增加虚拟内存方法
    centos下kill、killall、pkill命令区别
    正则表达式全集
    Mysql中外键的 Cascade ,NO ACTION ,Restrict ,SET NULL
    配置frp实现内网穿透
    解决微信授权回调页面域名只能设置一个的问题 [php]
    高性能Mysql主从架构的复制原理及配置详解
    【Git】工作中99%能用到的git命令
    SVN服务器搭建和使用
  • 原文地址:https://www.cnblogs.com/xss512/p/10523568.html
Copyright © 2011-2022 走看看