集合的理解

集合

在这里插入图片描述

集合框架

Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组
|----ArrayListLinkedListVector
|----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”
|----HashSetLinkedHashSetTreeSet
Map接口:双列集合,用来存储一对(key - value)一对的数据 -->高中函数:y = f(x)
|----HashMapLinkedHashMapTreeMapHashtableProperties

Ilterator迭代器接口

集合元素的遍历操作,使用迭代器Iterator接口
1.内部的方法:hasNext()next()
2.集合对象每次调用iterator()方法都得到一个全新的迭代器对象,
默认游标都在集合的第一个元素之前。
3.内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()

Collection子接口一:List

List接口框架
Collection接口:单列集合,用来存储一个一个的对象
|----List接口:存储有序的、可重复的数据。 -->“动态”数组,替换原有的数组

ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementData存储
LinkedList:对于频繁的插入、删除操作,使用此类效率比ArrayList高;底层使用双向链表存储(主要是头部和中间插入)
Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementData存储
ArrayList的源码分析:
    
2.1 jdk 7情况下
ArrayList list = new ArrayList();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementData[0] = new Integer(123);
…
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。
默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
结论:建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
    
2.2 jdk 8ArrayList的变化:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{}.并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建了长度10的数组,并将数据123添加到elementData[0]
…
后续的添加和扩容操作与jdk 7 无异。
    
2.3 小结:jdk7中的ArrayList的对象的创建类似于单例的饿汉式,而jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存。
LinkedList的源码分析:
LinkedList list = new LinkedList(); 内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象。其中,Node定义为:体现了LinkedList的双向链表的说法

Vector的源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组。
在扩容方面,默认扩容为原来的数组长度的2倍。
面试题:ArrayListLinkedListVector三者的异同?
同:三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
不同:见上
查找复杂度:ArrayListo(1),LinkedListo(n)
删除复杂度:ArrayListo(n),LinkedListo(1)

在这里插入图片描述

在这里插入图片描述

Collection子接口一:Set

Set接口的框架:
Collection接口:单列集合,用来存储一个一个的对象
|----Set接口:存储无序的、不可重复的数据 -->高中讲的“集合”

HashSet:作为Set接口的主要实现类;线程不安全的;可以存储null
LinkedHashSet:作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历(添加了两个引用,记录前后数据)。 对于频繁的遍历操作,LinkedHashSet效率高于HashSet.
TreeSet:可以按照添加对象的指定属性,进行排序。
1.Set接口中没有额外定义新的方法,使用的都是Collection中声明过的方法。
2.要求:向Set(主要指:HashSetLinkedHashSet)中添加的数据,其所在的类一定要重写hashCode()equals()
要求:重写的hashCode()equals()尽可能保持一致性:相等的对象必须具有相等的散列码。
重写两个方法的小技巧:对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。
一、Set:存储无序的、不可重复的数据
HashSet为例说明:
1. 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
2. 不可重复性:保证添加的元素按照equals()判断时,不能返回true.即:相同的元素只能添加一个。
二、添加元素的过程:以HashSet为例:
我们向HashSet中添加元素a,首先调用元素a所在类的hashCode()方法,计算元素a的哈希值, 此哈希值接着通过某种算法计算出在HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素:

如果此位置上没有其他元素,则元素a添加成功。 —>情况1

如果此位置上有其他元素b(或以链表形式存在的多个元素),则比较元素a与元素b的hash值:
----如果hash值不相同,则元素a添加成功。—>情况2
----如果hash值相同,进而需要调用元素a所在类的equals()方法:
-------- equals()返回true,元素a添加失败
--------equals()返回false,则元素a添加成功。—>情况3

对于添加成功的情况2和情况3而言:元素a 与已经存在指定索引位置上数据以链表的方式存储。
jdk 7 :元素a放到数组中,指向原来的元素。
jdk 8 :原来的元素在数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表的结构。
TreeSet
1.TreeSet中添加的数据,要求是相同类的对象。
2.两种排序方式:自然排序(实现Comparable接口) 和 定制排序(Comparator3.自然排序中,比较两个对象是否相同的标准为:compareTo()返回0.不再是equals().
4.定制排序中,比较两个对象是否相同的标准为:compare()返回1.不再是equals().

Map接口

一、Map的实现类的结构:
Map:双列数据,存储key-value对的数据 —类似于高中的函数:y = f(x)

HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
------LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。
原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。 对于频繁的遍历操作,此类执行效率高于HashMapTreeMap:保证按照添加的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

面试题:

1.在jdk1.7中,在多线程环境下,扩容时会造成环形链或数据丢失。
2.在jdk1.8中,在多线程环境下,会发生数据覆盖的情况。
HashMap的底层实现原理?
答:HashMap实例化后。底层是一个长度是16的一维数组,jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。

HashMapHashtable的异同?
答:HashMap实现了的接口是AbstractMapHashTable底层实现了Dictionary接口,不过相同点都是实现Map<K,V>, Cloneable, Serializable的接口,相对HashTable,HashMap的效率要快,但是线程不安全,HashTable中的size,isEmpty都有进行synchronized同步锁锁定。
    
CurrentHashMapHashtable的异同?
三、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在底层实现方面的不同:
new HashMap():底层没有创建一个长度为16的数组
jdk 8底层的数组是:Node[],而非Entry[]
首次调用put()方法时,底层创建长度为16的数组
jdk7底层结构只有:数组+链表。jdk8中底层结构:数组+链表+红黑树。
4.1 形成链表时,七上八下(jdk7:新的元素指向旧的元素。jdk8:旧的元素指向新的元素)
4.2 当数组的某一个索引位置上的元素以链表形式存在的数据个数 > 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

JUC集合线程安全

List

package com.zzc.unsafe;

import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * @author: zzc
 * @create: 2021-11-21  22:43:30
 **/
//java.util.ConcurrentModificationException  并发修改异常!
public class ListTest {

    public static void main(String[] args) {

        //并发下ArrayList是不安全的
        /**
         * 解决方案:
         * 1、List<String> list = new Vector<>();
         * 2、List<String> list = Collections.synchronizedList(new ArrayList<>());
         * 3、List<String> list = new CopyOnWriteArrayList<>();
         * CopyOnWrite写入时复制
         * 多个线程调用的时候,list,读取的时候,固定的,写入(覆盖)
         * 在写入的时候避免覆盖,造成数据问题
         * 读写分离
         */
        List<String> list = new CopyOnWriteArrayList<>();
        for (int i = 1; i <=50; i++) {
            new Thread(()->{
                list.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
    }

}

Set

package com.zzc.unsafe;

import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * @author: zzc
 * @create: 2021-11-21  22:56:54
 **/
//java.util.ConcurrentModificationException 并发修改异常
public class SetTest {

    public static void main(String[] args) {

        /**
         * HashSet 底层是 HashMap 线程不安全
         *
         * 解决方案:
         * 1、Set<String> hashSet = Collections.synchronizedSet(new HashSet<>());
         * 2、Set<String> hashSet = new CopyOnWriteArraySet<>();
         *
         *
         */
        Set<String> hashSet = new CopyOnWriteArraySet<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                hashSet.add(UUID.randomUUID().toString().substring(0,5));
                System.out.println(hashSet);
            },String.valueOf(i)).start();
        }
    }
}

Map

package com.zzc.unsafe;

import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: zzc
 * @create: 2021-11-21  23:08:40
 **/
//java.util.ConcurrentModificationException 并发修改异常
public class MapTest {


    public static void main(String[] args) {

        /**
         * HashMap 线程不安全
         * 解决方案:
         * 1、Map<String, String> hashMap = new Hashtable<>();
         * 2、Map<String, String> hashMap = Collections.synchronizedMap(new HashMap<>());
         * 3、Map<String, String> hashMap = new ConcurrentHashMap<>();
         */
        Map<String, String> hashMap = new ConcurrentHashMap<>();
        for (int i = 0; i < 30; i++) {
            new Thread(()->{
                hashMap.put(Thread.currentThread().getName(),
                        UUID.randomUUID().toString().substring(0,5));
                System.out.println(hashMap);
            },String.valueOf(i)).start();
        }
    }
}

for (int i = 0; i < 30; i++) {
new Thread(()->{
hashMap.put(Thread.currentThread().getName(),
UUID.randomUUID().toString().substring(0,5));
System.out.println(hashMap);
},String.valueOf(i)).start();
}
}
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值