zoukankan      html  css  js  c++  java
  • Java-集合

    一、Java集合框架概述

    ●一方面,面向对象语言对事物的体现都是以对象的形式,为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用Array存储对象方面具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中。

    ➢数组在内存存储方面的特点:

    数组初始化以后,长度就确定了。
    数组声明的类型,就决定了进行元素初始化时的类型

    ➢数组在存储数据方面的弊端:

    数组初始化以后,长度就不可变了,不便于扩展
    数组中提供的属性和方法少,不便于进行添加、删除、插入等操作,且效率不高。同时无法直接获取存储元素的个数
    数组存储的数据是有序的、可以重复的。--->存储数据的特点单一

    ●Java集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。

    二、集合框架

    ●Java集合可分为CollectionMap两种体系

    Collection接口:单列数据,定义了存取一组对象的方法的集合

    List:元素有序、可重复的集合 --> “动态”数组

    ArrayList、LinkedList、Vector

    Set: 元素无序、不可重复的集合

    HashSet、LinkedHashSet、TreeSet

    Map接口:双列数据,保存具有映射关系“key-value对”的集合

    HashMap、LinkedHashMap、TreeMap、Hashtable、Properties

    Collection接口继承树

    Map接口继承树

    三、Collection接口

    1.Collection接口常用的15种方法

    package com.xudong.java;
    
    import org.junit.Test;
    
    import java.util.*;
    
    public class CollectionTest {
        @Test
        public void test1(){
            Collection coll = new ArrayList();
    
            //1.add():将元素e添加到coll中
            coll.add("AA");
            coll.add("BB");
            coll.add(123);//自动装箱
            coll.add(new Date());
    
            //2.size():获取添加的元素的个数
            System.out.println(coll.size());
    
            //3.addAll(Collection coll1):将coll1集合中的元素添加到当前的集合中
            Collection coll1 = new ArrayList();
            coll1.add(456);
            coll1.add("CC");
            coll.addAll(coll1);
    
            System.out.println(coll.size());
            System.out.println(coll);
    
            //4.clear():清空集合元素
            coll.clear();
    
            //5.isEmpty():判断当前集合是否有元素
            System.out.println(coll.isEmpty());
        }
    
        @Test
        //注意:向Collection接口的实现类的对象中添加数据obj时,要求所在类要重写equals()
        public void test2(){
           Collection coll = new ArrayList();
           coll.add(123);
           coll.add(456);
           coll.add(new String("Tom"));
           coll.add(false);
           coll.add(new Person("Jerry",21));
    
            //6.contains(Object obj):判断当前集合中是否包含obj
            boolean contains = coll.contains("Jerry");
            System.out.println(contains);
    
            //7.containsAll(Collection coll1):判断形参coll1中的所有元素是否都存在于当前集合中。
            Collection coll1 = Arrays.asList(132,456,789);
            System.out.println(coll.containsAll(coll1));
    
            //8.remove(Object obj):从当前集合中移除obj元素。
            System.out.println(coll.remove(123));
    
            //9.removeAll(Collection coll1):从当前集合中移除coll1中的所有元素,差集
            coll.removeAll(coll1);
            System.out.println(coll);
            System.out.println(coll1);
    
            //10.retainAll():求交集
            coll.retainAll(coll1);
            System.out.println(coll);
    
            //11.equals():有序的比较两个集合。全等返回true
    
            //12.hashCode():返回当前对象的哈希值
    
            //13.toArray() 集合 ---> 数组
            Object[] arr = coll.toArray();
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
        }
    
        @Test
        public void test3(){
            Collection coll = new ArrayList();
            coll.add(123);
            coll.add(456);
            coll.add(new String("Tom"));
            coll.add(false);
            coll.add(new Person("Jerry",21));
    
            //14.toArray() 集合 ---> 数组
            Object[] arr = coll.toArray();
            for (int i = 0; i < arr.length; i++) {
                System.out.println(arr[i]);
            }
    
            //15.asList():数组 ---> 集合
            List<String> list = Arrays.asList(new String[]{"A", "C", "H", "M", "B"});//new 包装类的对象
            System.out.println(list);
        }
    }
    

    2.Iterator迭代器接口。遍历集合元素

    ●Iterator对 象称为迭代器(设计模式的一种),主要用于遍历Collection 集合中的元素。
    GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。类似于“公交车.上的售票员”、“火车上的乘务员”、“空姐”。
    ●Collection接口继承了java.lang.lterable接口,该接口有一个iterator()方法,那么所有实现了Collection接口的集合类都有一个iterator()方法,用以返回一个实现了Iterator接口的对象。
    Iterator仅用于遍历集合,Iterator本身并不提供承装对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合。
    集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。

    package com.xudong.java;
    
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.Collection;
    import java.util.Iterator;
    
    public class IteratorTest {
        @Test
        public void test1(){
            Collection coll = new ArrayList();
            coll.add(123);
            coll.add(456);
            coll.add(new String("Tom"));
            coll.add(false);
            coll.add(new Person("Jerry",21));
    
            //遍历集合种元素
            Iterator iterator = coll.iterator();
            System.out.println(iterator.next());
    
            //方法一:不推荐
    //        for (int i = 0; i < coll.size(); i++) {
    //            System.out.println(iterator.next());
    //        }
    
            //方法二:
            while (iterator.hasNext()){
                System.out.println(iterator.next());
            }
    
            //remove移除指定元素
            Iterator iter = coll.iterator();
            while (iter.hasNext()){
                Object obj = iter.next();
                if ("Tom".equals(obj)){
                    iter.remove();
                }
            }
    
            System.out.println(coll);
    
    
        }
    }
    

    Iterator新特性

    使用foreach循环遍历集合元素
    ●Java 5.0提供了foreach 循环迭代访问Collection和数组。
    ●遍历操作不需获取Collection或数组的长度,无需使用索引访问元素。
    遍历集合的底层调用Iterator完成操作
    ●foreach还可以用来遍历数组。

    package com.xudong.java;
    
    import org.junit.Test;
    
    import java.util.ArrayList;
    import java.util.Collection;
    
    public class forTest {
        @Test
        public void test(){
            Collection coll = new ArrayList();
            coll.add(123);
            coll.add(456);
            coll.add(new String("Tom"));
            coll.add(false);
            coll.add(new Person("Jerry",21));
    
            //for(集合元素类型 局部变量 : 集合对象)
            for (Object obj : coll){
                System.out.println(obj);
            }
    
        }
    
        @Test
        public void test1(){
            int[] arr = new int[]{1,2,33,2,5,4,6,7};
            //for(数组元素类型 局部变量 : 数组对象)
            for (int i : arr){ //将arr取到的值赋给i,数组内容不会改变。
                System.out.println(i);
            }
        }
    }
    

    3.List接口

    3.1 List接口概述

    ●鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组
    ●List集合类中元素有序、且可重复,集合中的每个元素索都有其对应的顺序索引。
    ●List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素。
    ●JDK API中List接口的实现类常用的有: ArrayList、LinkedList 和 Vector。

    ArrayList: 作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
    LinkedList: 对于频繁的插入、删除操作,使用此类效率E比ArrayList高; 底层使用双向链表存储
    Vector: 作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储

    3.2 ArrayList 源码分析

    jdk 7情况下

    ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData

    list.add(123);//eLementData[e] = new Integer(123);
    ...
    list.add(11);//如果此次的添加导致底层eLementData数组容量不够,则扩容。
    

    默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
    结论:建议开发中使用带参的构造器: ArrayList list = new ArrayList(int capacity)

    jdk 8中ArrayList的变化:

    ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
    list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到eLementData
    

    后续的添加和扩容操作与jdk 7无异。

    小结: jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8 中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。

    3.3 LinkedList 源码分析

    Linkedlist list = new LinkedList(); //内部声明了Node类型的first和last属性,默认值为null
    list. add(123);//将123封装到Node中,创建了Node对象。
    

    3.4 List接口方法

    ●List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来操作集合元素的方法。

    void add(int index, Object ele):在index位置插入ele元素
    boolean addAll(int index, Collection eles):从index位置开始将eles中的所有元素添加进来
    Object get(int index):获取指定index位置的元素
    int indexOf(Object obj):返回obj在集合中首次出现的位置
    int lastlndexOf(Object obj): 返回obj在当前集合中未次出现的位置
    Object remove(int index):移除指定index位置的元素,并返回此元素
    Object set(int index, Object ele):设置指定index位置的元素为ele
    List subList(int fromIndex, int tolndex):返回从fromIndex到toIndex位置的子集合

    4.Set接口

    4.1 Set接口概述

    ●Set接口是Collection的子接口,set接口没有提供额外的方法
    ●Set集合是无序的,不可重复的

    ➢无序性:不等于随机性。存储的数据根据哈希值存储
    ➢不可重复性:保证添加的元素按照equals()判断时,不能返回true。

    ●Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法

    4.2 Set实现类之一:HashSet

    ●HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个实现类。
    ●HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。本质上是数组与链表的结合体。
    HashSet具有以下特点:

    ➢不能保证元素的排列顺序
    ➢HashSet不是线程安全的
    ➢集合元素可以是null

    HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。
    ●对于存放在Set容器中的对象,对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则。即:“ 相等的对象必须具有相等的散列码”

    4.3 Set实现类之二:LinkedHashSet

    ●LinkedHashSet是HashSet的子类
    ●LinkedHashSet根据元素的hashCode值来决定元素的存储位置,但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。遍历其内部数据时,可以按照添加的顺序遍历。
    LinkedHashSet插入性能略低于HashSet,但在迭代访问Set里的全部元素时有很好的性能。
    ●LinkedHashSet不允许集合元素重复。

    4.4 Set实现类之三:TreeSet

    ●TreeSet是SortedSet接口的实现类,TreeSet 可以确保集合元素处于排序状态。
    ●TreeSet底层使用红黑树结构存储数据。可以按照添加对象的指定属性,进行排序。
    ●新增的方法如下:(了解)

    Comparator comparator()
    Object first()
    Object last()
    Object lower(Object e)
    Object higher(Object e)
    SortedSet subSet(fromElement, toElement)
    SortedSet headSet(toElement)
    SortedSet tailSet(fromElement)

    ●TreeSet两种排序方法:自然排序和定制排序。默认情况下,TreeSet采用自然排序。
    ●向TreeSet中添加的数据,要求是相同类的对象。

    添加元素的过程,以HashSet为例:

    我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值,
    此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:
          如果此位置上没有其他元素,则元素a添加成功。---> 情况1
          如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
                如果hash值不相同,则元素a添加成功。---> 情况2
                如果hash值相同,进而需要调用元素a所在类的equlas()方法:
                      equals()返回true,元素a添加失败
                      equaLs()返回false,则元素a添加成功。--->情况3
    
    对于添加成功的情况2和情况3而言:
          元素a与已经存在指定索引位置上数据以链表的方式存储。
                jdk 7 :元素a放到数組中,指向原来的元素。
                jdk 8 :原来的元素在数組中,指向元素a
    

    Map接口

    1.Map的实现类

    /----Map:双列数据,存储key-value对的数据--- 类似于高中的函数: y = f(x)
          /----HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null 的key和value
                /----LinkedHashMap:保证在遍历map元素时,可 以按照添加的顺序实现遍历。
                      原因:在原有的HashMap底层结构基础上,添加了一对指针,
                      指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高FHashMap。
          /---- TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或定制排序底层使用红黑树
          /----Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key value
                /----Properties: 常用来处理配置文件。key 和value都是String类型
          
           HashMap的底层:    数组+链表    (jdk7及之前)
                         数组+链表+红黑树 (jdk 8)
    

    Map结构的理解:

    Map中的key:无序的、不可重复的,使用Set存储所有的key ---> key所在的类要重写 equals() 和 HashCode() (HashMap为例)
    Map中的value:无序的、可重复的,使用Collection存储所有的value。---> value 所在的类要重写 equals()
    一个键值对: key-value 构成了一个Entry对象。
    Map中的entry:无序的、不可重复的,使用Set存储所有的entry

    2.Map接口常用的方法:

    添加、删除、修改操作:

    ➢Object put(Object key,Object value):将指定key-value添加到(或修改)当前map对象中
    ➢void putAI(Map m):将m中的所有key-value对存放到当前map中
    ➢Object remove(Object key):移除指定key的key-value对,并返回value
    ➢void clear():清空当前map中的所有数据

    元素查询的操作:

    ➢Object get(Object key):获取指定key对应的value
    ➢boolean containsKey(Object key):是否包含指定的key
    ➢boolean containsValue(Object value):是否包含指定的value
    ➢int size():返回map中key-value对的个数
    ➢boolean isEmpty():判断当前map是否为空
    ➢boolean equals(Object obj):判断当前map和参数对象obj是否相等

    元视图操作的方法:

    ➢Set keySet():返回所有key构成的Set集合
    ➢Collection values():返回所有value构成的Collection集合
    ➢Set entrySet():返回所有key-value对构成的Set集合

    3.HashMap的底层实现原理

    ➢JDK7.0
    HashMap map = new HashMap():
    在实例化以后,底层创建了长度是16的一维数组Entry[] table.
    ...可能已经执行过多i次put...
    map.put (key1, value1):
    首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry 数组中的存放位置。如果此位置上的数据为空,此时的key1-value1 添加成功。---- 情况1
    ●如果此位置上的数据不为空,( 意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
          ●如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1 添加成功。---- 情况2
          ●如果key1的哈希值和已经存在的某一个数据(key2-value2) 的哈希值相同,继续比较:调用key1所在类的equals(key2)
                ●如果equals()返回false:此时key1-value1添加成功。---- 情况3
                ●如果equaLs()返回true:使用value1替换value2。
    
          补充:关于情况2和情况3:此时key1-value1 和原来的数据以链表的方式存储。
          在不断添加的过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空)时,扩容。
          默认的扩容方式:扩容为原来容量的2倍,并将原有的数据复制过来。
    
    ➢jdk8相较于jdk7在底层实现方面的不同:
    1. new HashMap():底层没有创建一个长度为16的数组
    2. jdk 8底层的数组是: Node[], 而非Entry[]
    3.首次调用put()方法时,底层创建长度为16的数组
    4. jdk7 底层结构只有:数组+链表。jdk8 中底层结构:数组+链表+红黑树。
    当数组的某一个索引位置上的元素以链表形式存在的数据个数> 8且当前数组的长度> 64时,此时此索引位置上的所有数据改为使用红黑树存储。
    

    4.HashMap源码中的重要常量

    **DEFAULT_INITIAL_CAPACITY **: HashMap的默认容量,16
    **MAXIMUM_CAPACITY **: HashMap的最大支持容量, 2^30
    DEFAULT_LOAD_FACTOR: HashMap的默认加载因子0.75
    TREEIFY_THRESHOLD: Bucket中链表长度大于该默认值8,转化为红黑树
    UNTREEIFY_THRESHOLD: Bucket中红黑树存储的Node小于该默认值,转化为链表MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量64。(当桶中Node的数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行) resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4倍。)
    table:存储元素的数组,总是2的n次幂
    entrySet:存储具体元素的集
    size: HashMap中存 储的键值对的数量
    modCount: HashMap扩容和结构改变的次数。
    threshold:扩容的临界值12,=容量填充因子 (160.75)= 12
    loadFactor:填充因子

    5.LinkedHashMap底层原理

    6.Properties

    ●Properties类是Hashtable的子类,该对象用于处理属性文件
    ●由于属性文件里的key、value 都是字符串类型,所以Properties里的key和value都是字符串类型
    ●存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

    五、Collections工具类

    Collections是一个操作Set、List 和Map等集合的工具类
    ●Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法
    排序操作: ( 均为static方法)

    reverse(List):反转List中元素的顺序
    shuffle(List):对List集合元素进行随机排序
    sort(List):根据元素的自然顺序对指定List集合元素按升序排序
    sort(List, Comparator): 根据指定的Comparator产生的顺序对List 集合元素进行排序
    swap(List, int, int): 将指定list集合中的i处元素和j处元素进行交换

    1.Collections常用方法

    查找、替换

    Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
    Object max(Collection,Comparator): 根据Comparator指定的顺序,返回给定集合中的最大元索
    Object min(Collection)
    Object min(Collection,Comparator)
    int frequency(Collection,Object): 返回指定集合中指定元素的出现次数
    void copy(List dest,List src):将src中的内容复制到dest中

    List dest = Array.asList(new Object[src.size()]);
    Sysout.out.println(dest.size);//src.size()
    Collections.copy(dest,src);
    Sysout.out.println(dest);
    

    boolean replaceAll(List list, Object oldVal, Object newVal):使用新值替换List对象的所有旧值

    同步控制

    ●Collections类中提供了多个synchronizedXxx()方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题

  • 相关阅读:
    【读书笔记】iOS-解析XML
    【读书笔记】iOS-iOS开发之iOS程序偏好设置(Settings Bundle)的使用
    【读书笔记】iOS-Settings Bundle
    【读书笔记】iOS-自定义 URL Scheme 完全指南
    【读书笔记】iOS-自定义URL Scheme注意事项
    【读书笔记】iOS-iCloud文件备份
    【读书笔记】iOS-iCloud介绍
    【读书笔记】iOS-后台运行模式
    【读书笔记】iOS-WiFi长连接
    【读书笔记】iOS-设置应用的硬件需求
  • 原文地址:https://www.cnblogs.com/nnadd/p/13384821.html
Copyright © 2011-2022 走看看