美文网首页
Java集合框架

Java集合框架

作者: bo132 | 来源:发表于2019-03-26 00:30 被阅读0次
    一.集合框架主要接口
    No 接口 说明
    1 Collection Collection 是层次结构 中的根接口。JDK 不提供此接口的任何直接 实现:它提供更具体的子接口(如 Set 和 List)实现。此接口通常用来传递 collection,并在需要最大普遍性的地方操作这些 collection。
    2 List Collection接口的子接口。List存储的元素允许重复。
    3 Set Collection接口的子接口。Set存储的元素不允许重复。
    4 Map 存放键值对的接口,接口中的每个元素以key—value形式存储。
    5 Queue 队列接口,此接口的子类可以实现队列操作
    6 Iterator 集合的输出接口,用于输出集合中的内容,只能从前到后的单向输出
    7 SortedSet 单值的排序接口,实现接口的集合类,里面的元素使用比较器排序
    8 SortedMap 键值对集合的排序接口,里面的元素按照key排序

    二. 集合框架对数据结构的应用

    1.简介
      在Java语言中,Java的设计者对常用的的数据结构和算法进行一系列的规范(接口)和类(类),所有抽象出来的数据结构和操作(算法)统称为Java集合框架。Java程序员在具体应用的时候,不必考虑数据结构和算法的实现细节,只需要使用具体的类便可以解决问题,大大提高了编程效率。这些数据结构在 java.util 包里,包含了 Collection、List、Set、Map、SortedMap 接口。这些接口的实现类有 LinkedList、TreeSet、ArrayList、HashMap 等。


    2.分类

    • 接口:Collection、List、Map 组成了集合框架中所有具体实现类的接口,它们定义了子类必须实现的方法,非常好记。
    • 实现:所有实现了上述3个接口的类,都被称作集合框架,实际上就是数据结构。比如 LinkedList、TreeSet 等
    • 算法:集合框架提供了很多可以直接调用的算法,比如求最大最小值、排序、填充等

    3.优点

    • 减少了工作量的同时增加了软件的可用性:不需要每个程序员手动实现排序,查找等数据结构操作
    • 执行速度更快更持久:集合框架的底层数据结构分为两类,基于节点和基于数组的,前者在频繁添加时效率更高,后者在频繁读取时候速度更快。
    • 互操作性与转换:由于实现了Collection接口,数据结构之间是可以互相转换的。可以clone,可以把现有的结构转成synchronized版本,还可以把基于链表的数据结构转换成基于数据的结构。

    4.缺点

    • 类型转换:在类型转换之间要考虑泛型类型的兼容性
    • 运行时类型检查:集合框架在运行时可能会抛出异常

    三.Java集合框架接口详细介绍

    1.Collection接口
      Collection是最基本的接口。一个Collection代表一组Object对象。Java不提供直接继承自Collection的类,Java提供的类都是继承子Collection的子类接口。例如List和Set。
      所有实现Collection的类都必须提供两个构造函数,一个无参的构造用于创建空的Collection,有一个Collection参数的构造函数用于创建一个新的Collection,这个新的Collection与传入的Collection有相同的元素。后一个构造函数允许用户复制一个Collection。
      不论Collection的实际类型如何,他都支持一个iterator()方法,该方法返回一个迭代子,使用该迭代子可逐一访问集合中的每一个元素。
      Collection接口派生的两个接口是List和Set。


    2.List
      List是一个有序的Collection接口,类似c语言的数组,使用该接口能够准确控制每个元素插入的位置。可以通过索引来访问List的每个元素。
      和Set不同的是,List可以插入相同的元素,而Set中不可以出现重复的元素。
      除了Collection的Iterator的接口,List还实现了listIterator()接口,多了增加,删除,修改向前向后遍历。
      实现List接口的常用类有LinkedList,ArrayList,Vector,Stack。


    2.1.LinkedList
      LinkedList实现了List接口,允许null元素。LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:

    Listlist=Collections.synchronizedList(newLinkedList(...));
    参考资料

    import java.util.List;
    import java.util.Iterator;
    import java.util.LinkedList;
    import java.util.NoSuchElementException;
    
    public class LinkedListTest {
        public static void main(String[] args) {
            // 测试LinkedList的API
            testLinkedListAPIs() ;
    
            // 将LinkedList当作 LIFO(后进先出)的堆栈
            useLinkedListAsLIFO();
    
            // 将LinkedList当作 FIFO(先进先出)的队列
            useLinkedListAsFIFO();
        }
        
        /*
         * 测试LinkedList中部分API
         */
        private static void testLinkedListAPIs() {
            String val = null;
            //LinkedList llist;
            //llist.offer("10");
            // 新建一个LinkedList
            LinkedList llist = new LinkedList();
            //---- 添加操作 ----
            // 依次添加1,2,3
            llist.add("1");
            llist.add("2");
            llist.add("3");
    
            // 将“4”添加到第一个位置
            llist.add(1, "4");
            
    
            System.out.println("\nTest \"addFirst(), removeFirst(), getFirst()\"");
            // (01) 将“10”添加到第一个位置。  失败的话,抛出异常!
            llist.addFirst("10");
            System.out.println("llist:"+llist);
            // (02) 将第一个元素删除。        失败的话,抛出异常!
            System.out.println("llist.removeFirst():"+llist.removeFirst());
            System.out.println("llist:"+llist);
            // (03) 获取第一个元素。          失败的话,抛出异常!
            System.out.println("llist.getFirst():"+llist.getFirst());
    
    
            System.out.println("\nTest \"offerFirst(), pollFirst(), peekFirst()\"");
            // (01) 将“10”添加到第一个位置。  返回true。
            llist.offerFirst("10");
            System.out.println("llist:"+llist);
            // (02) 将第一个元素删除。        失败的话,返回null。
            System.out.println("llist.pollFirst():"+llist.pollFirst());
            System.out.println("llist:"+llist);
            // (03) 获取第一个元素。          失败的话,返回null。
            System.out.println("llist.peekFirst():"+llist.peekFirst());
        
    
            System.out.println("\nTest \"addLast(), removeLast(), getLast()\"");
            // (01) 将“20”添加到最后一个位置。  失败的话,抛出异常!
            llist.addLast("20");
            System.out.println("llist:"+llist);
            // (02) 将最后一个元素删除。        失败的话,抛出异常!
            System.out.println("llist.removeLast():"+llist.removeLast());
            System.out.println("llist:"+llist);
            // (03) 获取最后一个元素。          失败的话,抛出异常!
            System.out.println("llist.getLast():"+llist.getLast());
    
    
            System.out.println("\nTest \"offerLast(), pollLast(), peekLast()\"");
            // (01) 将“20”添加到第一个位置。  返回true。
            llist.offerLast("20");
            System.out.println("llist:"+llist);
            // (02) 将第一个元素删除。        失败的话,返回null。
            System.out.println("llist.pollLast():"+llist.pollLast());
            System.out.println("llist:"+llist);
            // (03) 获取第一个元素。          失败的话,返回null。
            System.out.println("llist.peekLast():"+llist.peekLast());
    
             
    
            // 将第3个元素设置300。不建议在LinkedList中使用此操作,因为效率低!
            llist.set(2, "300");
            // 获取第3个元素。不建议在LinkedList中使用此操作,因为效率低!
            System.out.println("\nget(3):"+llist.get(2));
    
    
            // ---- toArray(T[] a) ----
            // 将LinkedList转行为数组
            String[] arr = (String[])llist.toArray(new String[0]);
            for (String str:arr) 
                System.out.println("str:"+str);
    
            // 输出大小
            System.out.println("size:"+llist.size());
            // 清空LinkedList
            llist.clear();
            // 判断LinkedList是否为空
            System.out.println("isEmpty():"+llist.isEmpty()+"\n");
    
        }
    
        /**
         * 将LinkedList当作 LIFO(后进先出)的堆栈
         */
        private static void useLinkedListAsLIFO() {
            System.out.println("\nuseLinkedListAsLIFO");
            // 新建一个LinkedList
            LinkedList stack = new LinkedList();
    
            // 将1,2,3,4添加到堆栈中
            stack.push("1");
            stack.push("2");
            stack.push("3");
            stack.push("4");
            // 打印“栈”
            System.out.println("stack:"+stack);
    
            // 删除“栈顶元素”
            System.out.println("stack.pop():"+stack.pop());
            
            // 取出“栈顶元素”
            System.out.println("stack.peek():"+stack.peek());
    
            // 打印“栈”
            System.out.println("stack:"+stack);
        }
    
        /**
         * 将LinkedList当作 FIFO(先进先出)的队列
         */
        private static void useLinkedListAsFIFO() {
            System.out.println("\nuseLinkedListAsFIFO");
            // 新建一个LinkedList
            LinkedList queue = new LinkedList();
    
            // 将10,20,30,40添加到队列。每次都是插入到末尾
            queue.add("10");
            queue.add("20");
            queue.add("30");
            queue.add("40");
            // 打印“队列”
            System.out.println("queue:"+queue);
    
            // 删除(队列的第一个元素)
            System.out.println("queue.remove():"+queue.remove());
        
            // 读取(队列的第一个元素)
            System.out.println("queue.element():"+queue.element());
    
            // 打印“队列”
            System.out.println("queue:"+queue);
        }
    }
    

    运行结果:

    
    Test "addFirst(), removeFirst(), getFirst()"
    llist:[10, 1, 4, 2, 3]
    llist.removeFirst():10
    llist:[1, 4, 2, 3]
    llist.getFirst():1
    
    Test "offerFirst(), pollFirst(), peekFirst()"
    llist:[10, 1, 4, 2, 3]
    llist.pollFirst():10
    llist:[1, 4, 2, 3]
    llist.peekFirst():1
    
    Test "addLast(), removeLast(), getLast()"
    llist:[1, 4, 2, 3, 20]
    llist.removeLast():20
    llist:[1, 4, 2, 3]
    llist.getLast():3
    
    Test "offerLast(), pollLast(), peekLast()"
    llist:[1, 4, 2, 3, 20]
    llist.pollLast():20
    llist:[1, 4, 2, 3]
    llist.peekLast():3
    
    get(3):300
    str:1
    str:4
    str:300
    str:3
    size:4
    isEmpty():true
    
    
    useLinkedListAsLIFO
    stack:[4, 3, 2, 1]
    stack.pop():4
    stack.peek():3
    stack:[3, 2, 1]
    
    useLinkedListAsFIFO
    queue:[10, 20, 30, 40]
    queue.remove():10
    queue.element():20
    queue:[20, 30, 40]
    
    

    2.2.ArrayList
      ArrayList实现了可变大小的数组,允许添加null。ArrayList是非同步的(unsynchronized)。每个ArrayList有一个容量大小,当元素不断增加到ArrayList里面时,容量随着元素的添加自动增加,由于每次增加元素判断增加长度会消耗时间,所以插入前我们可以调用ensureCapacity方法来增加ArrayList的容量,以便提高效率。

    ublic class ArrayListTest {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            
            list.add("Apple");
            list.add("Watermelon");
            list.add("Banana");
            list.add("Strawbarry");
            // for循环遍历打印输出
            for(String s : list) {
                System.out.println(s);
            }
            
            // 将ArrayList转化为数组,遍历打印
            String[] stringArrays = new String[list.size()];
            list.toArray(stringArrays);
            for(int i = 0; i<stringArrays.length;i++) {
                System.out.println(stringArrays[i]);
            }
            
            // 使用迭代器遍历
            Iterator<String> it = list.iterator();
            while(it.hasNext()) {
                System.out.println(it.next());
            }
            
            final int N=1000000;
            ArrayList<String> list1 = new ArrayList<String>();
            long start = System.currentTimeMillis();
            for(int i=0;i<N;i++) {
                list1.add("1");
            }
            System.out.println(System.currentTimeMillis()-start);
            
            ArrayList<String> list2 = new ArrayList<String>();
            long start2 = System.currentTimeMillis();
            list2.ensureCapacity(N); // 对ArrayList进行扩充
            for(int i=0;i<N;i++) {
                list2.add("1");
            }
            System.out.println(System.currentTimeMillis()-start2); // 执行结果比原先快18ms
        }
    }
    

    2.3.Vector
      Vector与ArrayList类似,不同的是Vector是同步的,允许重复元素,底层数据结构可变数组,由于线程问题,ArrayList检索的效率高于Vector。


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


    3.Set
      Set接口没有提供Collection 接口额外的方法,但实现Set接口的集合类中的元素是不可重复的。所以Set添加的元素必须定义equals()方法以确保对象的唯一性。Set接口不保证维护元素的次序。
       Set的实现类主要有:HashSet及其子类 LinkedHashSet

    • HashSet:不允许出现重复元素,允许包含null元素,因为不能重复,所以只能有一个null。
    package HashSetTest;
    
    import java.util.HashMap;
    import java.util.Iterator;
    import java.util.Map;
    
    public class HashSet {
        public static void main(String[] args) {
            Map<String,String> map = new HashMap<String,String>();
            map.put("1","value1");
            map.put("2","value2");
            // map 再put的话 会更新2的value
            map.put("2", "good");
            map.put("3","value3");
            
            //第一种,普遍使用
            System.out.println("通过Map.keySet遍历key何value:");
            for(String key :map.keySet()) {
                System.out.println("key=" + key + " and value= " + map.get(key));
            }
            
            // 通过Map.entrySet使用iterator遍历key和value
            System.out.println("通过Map.entrySet使用iterator遍历key和value:");
            Iterator<Map.Entry<String, String>> it = map.entrySet().iterator(); 
            while(it.hasNext()) {
                Map.Entry<String, String> enter = it.next();
                System.out.println("key= " + enter.getKey() + "value= " + enter.getValue());
            
            }
            //第三种:推荐,尤其是容量大时
             System.out.println("通过Map.entrySet遍历key和value");
            for(Map.Entry<String, String> entry:map.entrySet()) {
                System.out.println("key= " + entry.getKey() + "vaue= " + entry.getValue());
            }
            
            //第四种
            System.out.println("通过Map.values()遍历所有的value,但不能遍历key");
            for(String v : map.values()) {
                System.out.println("value: "+v);
            }
            
        }
    }
    

    参考资料

    • TreeSet:可以实现排序等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,并保证该集合元素组成按照“升序”排列。

    a)(在对大量信息进行检索的时候,TreeSet比AraayList更有效率,能保证在log(n)的时间内完成)。

    b)TreeSet是实用树形结构来存储信息的,每个节点都会保存一下指针对象,分别指向父节点,左分支,右分支,相比较而言,ArrayList就是一个含有元素的简单数组了,正因为如此,它占的内存也要比ArrayList多一些。

    c)想TreeSet插入元素也比ArrayList要快一些,因为当元素插入到ArrayList的任意位置时,平均每次要移动一半的列表,需要O(n)的时间, 而TreeSet深度遍历查询花费的实施只需要O(log(n))(普遍的都是,set查询慢,插入快,list查询快,插入满。)

    • LinkedHashSet:LinkedHashSet对元素的插入和删除效率比较高,对于检索效率要较低。
      LinkedHashSet:采用双向表链接的方式进行数据的存储,因此在存放元素时,会记录每个元素的HashCode值,输出时会根据HashCode值的顺序(先进先出)进行输出,但是LinkedHashSet在底层对于数据的存放和HashSet原理一致。

    4.Map
      Map接口有四个实现类,分别是HashMap Hashtable LinkedHashMap 和TreeMap,Map存储键值对,不允许键重复,允许值重复,允许值重复。

    • HashMap根据键的HashCode存储数据,具有很快的访问速度。HashMap不支持线程的同步,可以同时有多个线程写HashCode,可能会导致插入数据不一致。可以使用Collections的synchronizedMap方法使HashMap具有同步的能力。
    • Hashtable不允许插入的键为null,支持线程同步,只能有一个线程写入数据,所以速度比较慢。
    • LinkedHashMap可以使用迭代器遍历,可以记录数据的顺序,不过遍历的时候速度比HashMap慢。
    • TreeMap可以指定比较器对数据进行排序,默认升序。

    5.Queue
    Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了Deque接口。基本上,一个队列就是一个先入先出(FIFO)的数据结构。
    详细解释


    6.Collection
    Collections是针对集合类的一个帮助类。提供了一系列静态方法实现对各种集合的搜索、排序、线程完全化等操作。

    最常用的是ArrayList,HashSet,HashMap,Array。TreeXXX用于排序的比较多。底层实现

    相关文章

      网友评论

          本文标题:Java集合框架

          本文链接:https://www.haomeiwen.com/subject/kaevvqtx.html