java进阶(五)_集合

Java集合

1. JAVA集合框架概述

1.1概述

  • 集合、数组都是对多个数据进行存储操作的结构,简称Java容器

    说明:此时的存储,主要指内存层面的存储,不涉及持久化的存储

  • 数据在存储多个数据方面的特点

    • 一旦初始化后,其长度就确定了;
    • 数组一旦定义好,其元素的类型也就确定了。我们只能操作指定类型的数据;
    • 数组在存储多个数据方面的缺点:
      • 一旦初始化后,长度不可修改;
      • 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,并且效率不高;
      • 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用;
      • 数组存储数据的特点:有序,可重复。对于无序、不可重复的需求,不能满足。

1.2 集合框架

Java集合可分为Collection和Map两种体系:

  • Collection接口:单列数据,定义了存取一组对象的方法的集合
    • List:元素有序、可重复的集合,也就是存储有序的、可重复的数据。--->”动态“数组,替换原有的数组
      • ArrayList、LinkedList、Vector
    • Set:元素无序、不可重复的集合。-->高中讲的”集合“
      • HashSet、LinkedHashSet、TreeSet

  • Map接口:双列数据,保存具有映射关系”key-value“对的集合。-->高中讲的”函数“:y=f(x)
    • HashMap、LinkedHashMap、TreeMap、Hashtable、Properties

map

2. Collection接口中常用的方法

  • 添加

    • add(Object obj),注意添加的是对象!!添加的整型数据会自动装箱。
    • addAll(Collection coll)
  • 获取有效元素的个数

    • int size()
  • 清空集合

    • void clear()
  • 是否是空集合

    • boolean isEmpty()
  • 是否包含某个元素

    • boolean contains(Object obj):是通过equals方法 (也就是只是判断内容是否相同)判断是否包含同一个对象。
      • 要求:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
    • boolean containsAll(Collection c):也是调用元素的equals方法来比较的,拿两个集合的元素依次比较。
  • 删除

    • boolean remove(Object obj):是通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
    • removeAll(Collection coll):取当前集合的差集。从当前集合中移除coll中的所有的元素。
  • 取两个集合的交集

    • retainAll(Collection coll):把交集的结果存在当前集合中,不影响coll
  • 集合是否相等

    • boolean equals(Object obj):要想返回True,需要当前集合和形参集合中的元素都相同。
  • 转成对象数组

    • Object[] toArray()
    • 数组转成集合:调用Arrays类的静态方法asList()
  • 获取集合对象的哈希值

    • hashCode():返回当前对象的hash值。
  • 遍历

    • iterator():返回迭代器对象,用于集合遍历。即返回Iterator接口的实例,用于遍历集合元素。

3. Iterator迭代器接口

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

  • Iterator仅用于遍历集合,Iterator本身并不提供承载对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合

  • 集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前

  • 内部方法:hasNext() 和next()

    @Test
    public void test1(){
        Collection coll=new ArrayList();
        coll.add(123);//自动装箱
        coll.add(456);
        coll.add("吃饭");

        Iterator iterator=coll.iterator();
        //hasNext()判断是否还有下一个元素
        while(iterator.hasNext()){
            //next():①指针下移;将②下移后集合位置上的元素返回
            System.out.println(iterator.next());
            //iterator.remove();
        }
    }
  • 内部还定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()。
    • 如果还未调用next()或者上次调用next方法之后已经调用了remove方法,再调用remove都会报IllegalStateException

4.Collection子接口之一:List接口

4.0 List接口的各个实现类特点

List:元素有序、可重复的集合,也就是存储有序的、可重复的数据。--->”动态“数组,替换原有的数组

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

面试题:ArrayList、LinkedList、Vector的异同?

4.1 ArrayList源码分析

1)jdk 7情况下
      ArrayList list=new Arraylist();//底层创建了长度是10的Object[]数组elementData
      list.add(123);//elementDate[0]=new Integer(123);
      list.add(11);//如果此次的添加导致底层的elementData数组容量不够,则扩容
      默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
      * 结论:建议开发中使用带参的构造器:ArrayList list=new ArrayList(int capacity)
    (2)jdk 8情况下,ArrayList的变化:
      ArrayList list=new Arraylist();//底层 Object[] elementDate 初始化为{},并没有创建长度
     list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData数组中。
     * 后续添加与jdk7.0无异。
-   (3)小结:jdk 7中ArrayList 的对象的创建类似于单例的饿汉式,而jdk8.0中的ArrayList的对象的创建类似于单列的
*     懒汉式,延迟了数组的创建,节省内存。

4.2 LinkedList源码分析

	LinkedList list=new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
  	list.add(123);//将123封装到Node中,创建了Node对象
    //其中,Node的定义体现了LinkedList的双向链表的说法

4.3 Vector的源码分析

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

4.4 List接口方法

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

    • void add(int index, Object ele):在index位置插入ele元素
    • Object get(int index):获取指定index位置的元素
    • Object remove(int index):移除指定index位置的元素,并返回此元素
    • Object set(int index, Object ele):设置指定index位置的元素为ele
  • 总结:常用方法

    增:add(Object obj)

    删:remove(int index)/remove(Object obj):区别就是删索引对应的元素还是删除该对象

    改:set(int index, Object ele)

    查:get(int index)

    长度:size()

    遍历:①迭代器方式;②增强for循环;③普通循环

5.Collection子接口之一:Set接口

5.0 Set接口的实现类的特点

  • set接口没有提供额外的方法,使用的都是Collection中定义过的方法
  • Set集合不允许包含相同的元素
  • Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法

Set:存储无序的、不可重复的数据。-->高中讲的”集合“

	HashSet:作为Set接口的主要实现类;线程不完全的;可以存储null值
		LinkedHashSet:是HashSet的一个子类;遍历其内部数据时,可以按照添加的顺序遍历
	TreeSet:可以按照添加对象的指定属性,进行*排序*

5.1理解(以HashSet为例)

(1)Set的特点 (以HashSet为例)

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

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

  • 向HashSet中添加元素a,首先调用元素a所在类的hasCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法算出在HashSet底层数组中的存放位置(即:索引位置),判断数组此位置上是否已经有元素;
    • 如果此位置上没有其他元素,则元素a添加成功;---情况1
    • 如果此位置上有其他元素b(或者以链表形式存在的多个元素),则比较元素a和元素b的hash值:
      • 如果hash值不相同,则元素a添加成功。--情况2
      • 如果hash值相同,进而需要调用元素a所在类的equals()方法:
        • equals()返回ture,元素a添加失败
        • equals()返回false,则元素a添加成功。---情况3

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

jdk7:元素放到数组中,指向原来的元素

jdk8:原来的元素放到数组中,指向元素a

总结:七上八下

HashSet底层:数组+链表方式存储

(3)要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals();并且重写的两个方法要尽可能保持一致性。

5.2 LinkedHashSet的使用

LinkedHashSet作为HashSet的子类,在添加数据的同时,每一个数据还维护了两个引用,记录此数据的前一个数据和后一个数据。优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet。

5.3 TreeSet

  • 向TreeSet中添加的数据,要求是相同类的对象

  • 涉及到两种排序方式:

    • 自然排序(实现Comparable接口)
    • 定制排序(Comparator)
  • 自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals();

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

6. Map接口

6.0 Map接口的各个实现类特点和理解

(1)Map:双列数据,保存具有映射关系”key-value“对的集合。-->高中讲的”函数“:y=f(x)

  • HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value

    • LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap。
  • TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或者定制排序,底层使用红黑树。

  • Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value

    • Properties:是Hashtable的子类,常用来处理配置文件。key和value都是String类型。存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

HashMap的底层:数组+链表(jdk7及之前),数组+链表+红黑树(jdk8)

面试题

  1. HashMap的底层实现原理?
  2. HashMap和Hashtable的异同?
  3. CurrentHashMap与Hashtable的异同?

(2)Map结构的理解

Map中的key:无序、不可重复的,使用Set存储所有的key;

Map中的value:无序的,可以重复,使用Collection存储所有的value;

一个键值对:key-value构成了一个Entry对象;

Map中的entry:无序的、不可重复,使用Set存储所有的entry

6.1 HashMap的底层实现原理?以jdk7为例说明:

  • HashMap map=new HashMap():

    在实例化以后,底层创建了长度是16的一维数组Entry[ ] table,

    ……可能已经执行了多次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. jdk8底层的数组是:Node[],而非Entry[]

    3. 首次调用put( )方法时,底层创建长度为16的数组

    4. Jdk7底层结构只有:数组+链表。jdk8中的底层结构:数组+链表+红黑树

      当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储。

	DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
    DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
    threshold:扩容的临界值=容量*加载因子:16*0.75=12
    TREEIFY_THRESHOLD:Bucket中链表长度大于默认值,转化为红黑树:8
    MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64       

6.2 LinkedHashMap的底层实现原理

源码中:

 static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;//能够记录添加元素的先后顺序
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }

6.3 Map接口:常用方法

//1.添加、删除、修改操作
Object put(Object key,Object value);//将指定key-value添加到(或修改)当前map对象中
void putAll(Map m);//将m中所有的key-value对存放到当前map中
Object remove(Object key);//移除指定key的key-value对,并返回value
void clear();//清空当前map中的所有数据
//2.元素查询操作
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是否相等
//3.元视图操作的方法
Set keySet();//返回所有key构成的Set集合
Collection values();//返回所有value构成的Collection集合
Set entrySet();//返回所有key-value对构成的Set集合

总结常用方法

  • 添加: put(Object key,Object value);
  • 删除:remove(Object key);
  • 修改: put(Object key,Object value);
  • 查询:get(Object key);//获取指定key对应的value
  • 长度:size();
  • 遍历:keySet( )/values( )/entrySet( )

6.4 TreeMap两种添加方式的使用

向TreeMap中添加key-value,要求key必须是由同一个类创建的对象

因为要按照key进行排序:自然排序、定制排序

7. Collections工具类

  • Collections是一个操作Set、List和Map等集合的工具类,之前将操作数组的工具类:Arrays;

  • Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。

  • 排序操作:(均为static方法)

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

    • Object max(Collections):
    • Object max(Collections,Comparator):
    • Object min(Collections):
    • Object min(Collections,Comparator):
    • int frequency(Collections,Object );
    • void copy(List dest,List src);将src的内容复制到dest
    • boolean replaceAll(List list,Object newVal);
  • Collections常用方法:同步控制

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

面试题:Collections和Collection的区别?

posted @   日积月累,水滴石穿  阅读(39)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示