JavaSE:集合

    科技2022-07-11  102

    JavaSE:集合

    概述集合框架 Collection的使用Collection中的常见方法迭代器接口 Iterator Collection的子接口之一:List底层源码分析ArrayListLinkedListVector List接口中的常用方法 Collection子接口之二:SetLinkedHashSet的使用TreeSet的使用 Map 接口Map中的常用方法HashMap的底层原理具体流程JDK8与JDK7底层原理的异同扩容机制 LinkedHashMap 的使用 Collections 工具类的使用

    概述

            为了方便对多个对象的操作,就要对对象进行存储。另一方面,使用数组存储对象方面具有一些弊端,而Java集合就像一种容器,可以动态地把多个对象的引用放入容器中。

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

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

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

    数组出刷以后,长度就不可变了,不便于添加数组中提供 属性和方法少,不便于进行添加、删除、插入等操作,且效率不高数组存储的数据是有序的、可以重复的。存储的数据特点单一。

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

    集合框架

    Collection的使用

    Collection即单列集合。统一定义了一套单列集合的接口。

    Collection中的常见方法

    boolean add(E e);

    boolean add(E e);将元素e添加到集合中(E类型为泛型)

    int size();

    int size();获取集合中的元素个数

    boolean addAll(Collection<? extends E> c);

    boolean addAll(Collection coll);添加集合coll中的元素到调用此方法的集合中

    boolean isEmpty();

    boolean isEmpty();判断集合是否为空

    void clear();

    void clear();清空集合

    boolean contains(Object o);

    boolean contains(Object o);判断当前集合中是否包含o

    boolean containsAll(Collection<?> c);

    boolean containsAll(Collection<?> c); 判断集合c中的所有元素是否都在调用此方法的集合中

    boolean remove(Object o);

    boolean remove(Object o);移除集合中的元素o

    boolean removeAll(Collection<?> c);

    boolean removeAll(Collection<?> c); 差集:从当前集合中移除集合c中的所有元素

    boolean retainAll(Collection<?> c);

    boolean retainAll(Collection<?> c);交集:查询此集合中coll中也包含的元素

    Object[] toArray();

    Object[] toArray();将此集合转换为Object类型的数组

    迭代器接口 Iterator

    Iterator对象称为迭代器(设计模式的一种),主要用于遍历Collection集合中的元素。

    GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露改对象的内部细节。迭代器模式,就是为容器而生。

    常用的遍历集合方法

    public static void main(String[] args) { Collection coll = new ArrayList(); coll.add(321); coll.add(12); coll.add("java"); coll.add("abc"); coll.add(new Date()); coll.add(new Person("张三",21)); Iterator iterator = coll.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); } }

    运行结果

    迭代器Iterator执行原理

    Collection的子接口之一:List

    List接口:存储有序的、可重复的数据。 ArrayList:作为List接口的主要实现类,线程不安全,执行效率高。底层使用Object类型的数组存储。LinkedList:相对于ArrayList,不同的是底层使用的是双向链表存储的。对于频繁的插入、删除操作,使用此类相率比ArrayList高。Vector:作为List接口的古老实现类,JDK1.0已存在。线程安全的,执行效率低。底层使用Object类型的数组存储。

    底层源码分析

    ArrayList

    JDK7 版本下: 当直接使用空参构造器创建对象

    List list = new ArrayList();

    底层创建了长度是10的 Object[] 数组 elementData。

    扩容问题 当添加数据导致底层数组的容量不足时,则要对底层数组进行扩容。 默认情况下扩容为原来的1.5倍,同时需要将原有数组中的数据复制到新的数组中。如果扩容为1.5倍之后容量仍然不足,将使用数据长度为新的容量长度。

    JDK8 版本下: 使用空参构造器直接创建对象,并不会立即创建长度为10的数组。size为0。 而是在调用add方法添加数据时开始创建数组。 DEFAULT_CAPACITY 为 10。 底层扩容机制

    LinkedList

    LinkedList list = new LinkedList();

    内部声明了Node类型的 first 和 last 属性,默认值为null

    list.add(123);

    将123封装到Node中,创建了Node对象。

    Vector

    Vector的源码:在jdk7和jdk8中,通过Vector()构造器创建对象时,底层都创建了长度为10的数组。在扩容方面,默认扩容为原来的数组长度的2倍。

    List接口中的常用方法

    由于List是有序的集合,于是在List接口中提供了不少的基于角标的方法。

    void add(int index, E element); boolean addAll(int index, Collection<? extends E> c); E get(int index); int indexOf(Object o); int lastIndexOf(Object o); E remove(int index); E set(int index, E element); List<E> subList(int fromIndex, int toIndex);

    Collection子接口之二:Set

    set接口:存储无序的、不可重复的数据 HashSet:作为Set接口的主要实现类:线程不安全:可以存储null值 LinkHashSet:作为HashSet的子类:遍历其内部数据时,可以按照添加的顺序遍历 TreeSet:可以按照添加对象的指定属性,进行排序

    set中没有定义新的方法,使用的都是Collection中的方法。

    无序性不代表随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数组的哈希值决定的不可重复性:保证添加的元素按照equals()判断时,不能返回true,即:相同的元素只能添加一个

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

    外面向HashSet中添加元素a,首先用元素a所在类的hashCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

    ​ 如果此位置上没有其他元素,则元素a添加成功

    ​ 如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值。

    ​ 如果hash值不同,则元素a添加成功。(情况2)

    如果hash值相同,进而需要调用元素a所在类的equals()方法,equals返回true,元素a添加失败,返回false,则元素a添加成功。(情况3)

    对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。

    不同JDK版本中链表存储数据的方式: jdk 7 :元素a放在数组中,指向原来的元素

    jdk 8:原来的元素在数组中,指向元素a

    HashSet是Set接口的典型实现,大多数时候使用Set集合时都使用这个类。

    HashSet按Hash算法来存储集合中的元素,因此具有很好的存取、查找、删除性能。

    HashSet具有以下特点:

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

    HashSet集合判断两个元素相等的标准:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。

    对于存放在Set容器中的对象,对应的类一定要重写equals和hashCode方法,以实现对象相等规则。

    LinkedHashSet的使用

    LinkedHashSet 作为HashSet的子类,在添加数据的同时,每个数据还维护了两个引用,记录此数据前一个数据和后一个数据。

    优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet

    TreeSet的使用

    向TreeSet中添加的数据,要求是相同类的对象。两种排序方式:自然排序 和定制排序

    自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals();

    定制排序中,比较两个对象是否相同的标准为:compare()还会0,不再是equals()

    Map 接口

    Map接口用于存储双列数据,以键值对(key-value)的形式存储。

    HashMap:作为Map的主要实现类;线程不安全的,效率高;可以存储null的key和value LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的频繁的遍历操作,此类执行效率高于HashMap。 TreeMap:保证按照添加的key-value进行排序,实现排序遍历。(按照key排序),底层使用红黑树。Hashtable:作为古老的实现类:线程安全,效率低 Properties:常用来处理配置文件。key和value都是String类型

    Map中的常用方法

    将指定key-value添加(修改)到当前Map对象中

    public V put(K key, V value)

    将集合m中的所有key-value添加到当前Map对象中

    public void putAll(Map<? extends K, ? extends V> m)

    将指定key的key-value从当前Map对象中删除

    public V remove(Object key)

    清空当前Map对象

    public void clear()

    获取指定key对应的value

    public V get(Object key)

    查询是否包含指定key

    public boolean containsKey(Object key)

    查询是否包含指定value

    public boolean containsValue(Object value)

    查询当前Map对象中key-value的个数

    public int size()

    判断当前Map对象是否为空

    public boolean isEmpty()

    将当前Map对象中的所有key存放到Set集合中返回

    public Set<K> keySet()

    将当前Map对象中所有value存放到Collection 集合中返回

    public Collection<V> values()

    将当前Map对象中的所有key-value存放到Set集合中返回

    public Set<Map.Entry<K,V>> entrySet()

    HashMap的底层原理

    在JDK 8中

    使用空参构造器创建对象。

    HashMap<Object, Object> map = new HashMap<>();

    底层并没有立即创建数组。loadFactor为加载因子,用于底层数组扩容,DEFAULT_LOAD_FACTOR为默认加载因子,值为0.75。 添加键值对操作。 哈希值计算 添加键值对的实现 可以看到,在底层中,键值对是存放在Node对象里,底层里Node数组实现。

    具体流程

    map.put(key1,value1);添加键值对。

    首先,调用key1所在类的hashCode()计算key1哈希值,此哈希值经过某种算法计算以后,得到在Node数组中的位置。如果此位置上数据为空,此时的key1 - value1添加成功。 — 情况1

    如果此位置上的数据不为空,(意味着此位置上存放在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:

    如果key1的哈希值与已经存在的数据的哈希值都不同,此时key1 - value1 添加成功。 — 情况2

    如果key1的哈希值和已经存在的某一个数据(key2 - value2)的哈希值相同,继续比较:调用key1所在类的equals方法,比较方式:

    如果equals()返回false:此时key1 - value1添加成功 — 情况3

    如果equals()返回true:使用value1替换value2.

    JDK8与JDK7底层原理的异同

    在调用空参构造器创建对象时,就已经创建了长度为16的数组,JDK8是在首次调用put方法添加数据的时候才创建数组的。JDK7底层中数据是存储在Entry数组中的,JDK8是存储在Node数组中的。JDK7底层结构实现使用的是:数据+链表;JDK8底层实现结构是:数组+链表+红黑树

    扩容机制

    在不断的添加过程中,会涉及到扩容问题,当超出临界值(且要存放的位置非空时)扩容,默认的扩容方式:扩容为原来的2倍,并将原有的数据复制过来。

    在JDK 7 中底层结构使用数组+链表,当出现哈希冲突问题时,相同哈希值的数据以链表的形式存储,并且使用的是头插的方式,新添加的数据指向数组中已存储的数据。在JDK 8中底层结构使用数组+链表+红黑树,当出现哈希冲突问题时,一开始使用链表形式存储,但是与JDK7不同的是,使用的是尾插的方式,新添加的数据存储到链表的尾部,原本的数据仍然存储在数组中。当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 8 且当前数组的长度 > 64时,此时索引位置上的所有数据改为使用红黑树存储。

    LinkedHashMap 的使用

    LinkedHashMap 是HashMap的子类。继承了HashMap,在HashMap的基础上添加了两个指针,保证在遍历map元素时,可以按照添加的顺序实现遍历。便于频繁的遍历操作。

    底层中创建Node对象,但创建的是一个Entry对象 此Entry继承了Node,在Node基础上添加了两个指针。

    Collections 工具类的使用

    Collections是操作Collection、Map的工具类

    Collections 工具类相关API

    使用自然排序,对list集合进行排序

    public static <T extends Comparable<? super T>> void sort(List<T> list)

    使用定制排序,对list集合进行排序

    public static <T> void sort(List<T> list, Comparator<? super T> c)

    将list集合中的元素反转

    public static void reverse(List<?> list)

    将list集合中的元素随机排序

    public static void shuffle(List<?> list)

    将list集合中角标为i和角标为j的元素对调

    public static void swap(List<?> list, int i, int j)

    根据自然排序返回给定集合中最大的元素

    public static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll)

    根据定制排序返回给定集合中最大的元素

    public static <T> T max(Collection<? extends T> coll, Comparator<? super T> comp)

    根据自然排序返回给定集合中最小的元素

    public static <T extends Object & Comparable<? super T>> T min(Collection<? extends T> coll)

    根据定制排序返回给定集合中最小的元素

    public static <T> T min(Collection<? extends T> coll, Comparator<? super T> comp)

    将集合src中的元素复制到dest集合中

    public static <T> void copy(List<? super T> dest, List<? extends T> src)

    使用 newVal 替换 lsit集合中的所有 oldVal

    public static <T> boolean replaceAll(List<T> list, T oldVal, T newVal)

    返回对象o在指定集合中出现的次数

    public static int frequency(Collection<?> c, Object o)
    Processed: 0.009, SQL: 8