Java集合类

ArrayList

juc线程安全

  1. Vector
  2. Collections.synchronizedList();
  3. new CopyOnWriteArrayList();

ArrayList 底层就是⼀个 Object[] 数组

ArrayList 底层数组默认初始化容量为 10
1、jdk1.8 中 ArrayList 底层先创建⼀个⻓度为 0 的数组
2、当第⼀次添加元素(调⽤ add() ⽅法)时,会初始化为⼀个⻓度为 10 的数组

当 ArrayList 中的容量使用完之后,则需要对容量进⾏扩容:
1、ArrayList 容量使⽤完后,会“⾃动”创建容量更⼤的数组,并将原数组中所有元素拷⻉过去,这会导致效率降低
2、优化:可以使⽤构造⽅法 ArrayList (int capacity) 或 ensureCapacity(int capacity) 提供⼀个初始化容量,避免
刚开始就⼀直扩容,造成效率较低

优点:

  1. 向 ArrayList 末尾添加元素(add() ⽅法)时,效率较⾼
  2. 查询效率⾼
    缺点:
  3. 扩容会造成效率较低(可以通过指定初始化容ᰁ,在⼀定程度上对其进⾏改善)
  4. 另外数组⽆法存储⼤数据ᰁ(因为很难找到⼀块很⼤的连续的内存空间)
  5. 向 ArrayList 中间添加元素(add(int index)),需要移动元素,效率较低
  6. 但是,向 ArrayList 中国位置增/删元素的情况较少时不影响;
  7. 如果增/删操作较多,可考虑改⽤链表

Linklist

LinkedList 底层是⼀个双向链表

优点: 增/删效率⾼
缺点: 查询效率较低
LinkedList 也有下标,但是内存不⼀定是连续的(类似C++重载[]符号,将循位置访问模拟为循秩访问)
LinkedList 可以调⽤ get(int index) ⽅法,返回链表中第 index 个元素

但是,每次查找都要从头结点开始遍历

Vector

**Vector 底层是数组,初始化容量为 10 **

扩容: 原容量使⽤完后,会进⾏扩容。新容量扩⼤为原始容量的 2 倍

Vector 是线程安全的(⾥⾯⽅法都带有 synchronized 关键字),效率较低,现在使⽤较少

如何将 ArrayList 变成线程安全的?
调⽤ Collections ⼯具类中的 static List synchronizedList(List list) ⽅法

Set

juc线程安全

  1. Collections.synchronizedSet();
  2. new CopyOnWriteArraySet();

1、jdk 1.5 引入,之前都是使用 Object[],引入后使用泛型

2、使⽤ Object[] 的缺点(2个)

  • 获取⼀个值时必须进⾏强制类型转换
  • 调⽤⼀个⽅法前必须使⽤ instanceof 判断对象类型

HashSet

HashSet实际上是一个HashMap实例,都是一个存放链表的数组。

它不保证存储元素的迭代顺序;此类允许使用null元素。

HashSet中不允许有重复元素 ,这是因为HashSet是基于HashMap实现的,HashSet中的元素都存放在HashMap的key上面,而value中的值都是统一的一个固定对象private static final Object PRESENT = new Object();

HashSet中add方法调用的是底层HashMap中的put()方法,而如果是在HashMap中调用put,首先会判断key是否存在,如果key存在则修改value值,如果key不存在这插入这个key-value。而在set中,因为value值没有用,也就不存在修改value值的说法.

因此往HashSet中添加元素,首先判断元素(也就是key)是否存在,如果不存在这插入,如果存在着不插入,这样HashSet中就不存在重复值。

所以判断key是否存在就要重写元素的类的equals()和hashCode()方法,当向Set中添加对象时,首先调用此对象所在类的hashCode()方法,计算次对象的哈希值,此哈希值决定了此对象在Set中存放的位置;若此位置没有被存储对象则直接存储,若已有对象则通过对象所在类的equals()比较两个对象是否相同,相同则不能被添加。

TreeSet

TreeSet是JAVA中集合的一种,TreeSet 是一个有序的集合,它的作用是提供有序的Set集合。

它继承于AbstractSet抽象类,实现了NavigableSet,Cloneable,java.io.Serializable接口。

一种基于TreeMap的NavigableSet实现。

因为TreeSet继承了AbstractSet抽象类,所以它是一个set集合,可以被实例化,且具有set的属性和方法。

TreeSet是基于TreeMap实现的。

TreeSet中的元素支持2种排序方式:自然排序 或者 根据创建TreeSet 时提供的 Comparator 进行排序。这取决于使用的构造方法。

TreeSet的性能比HashSet差,但是我们在需要排序的时候可以用TreeSet,因为他是自然排序也就是升序。下面是TreeSet实现代码,TreeSet只能通过迭代器迭代元素

Map

juc线程安全

  1. Collections.synchronizedMap();
  2. new ConcurruentHashMap();

**Map 以 (key ,value) 的形式存储数据:键值对 key 和 value 存储的都是对象的内存地址(引⽤) **

Map.Entry<K, V> 是 Map 的⼀个接⼝。接⼝中的内部接⼝默认是 public static 的。

遍历方式

  1. map.keySet(),通过key取value
  2. map.entrySet(),取出key和value(推荐)

HashMap

HashMap 底层是⼀个数组,数组中每个元素是⼀个单向链表(即,采⽤拉链法解决哈希冲突)

单链表的节点每个节点是 Node<K, V> 类型

同⼀个单链表中所有 Node 的 hash值不⼀定⼀样,但是他们对应的数组下标⼀定⼀样(数组下标利⽤哈希函数/哈希算法根据 hash值计算得到的 )

HashMap 是数组和单链表的结合体

  1. HashMap 默认初始化容量: 16
  2. 必须是 2 的次幂,这也是 jdk 官⽅推荐的
  3. 这是因为达到散列均匀,为了提⾼ HashMap 集合的存取效率,所必须的
  4. HashMap 默认加载因⼦:0.75
    数组容量达到 3/4 (160.75)时,开始扩容2
  5. JDK 8 之后,对 HashMap 底层数据结构(单链表)进⾏了改进
  6. 如果单链表元素超过8个,则将单链表转变为红⿊树;
  7. 如果红⿊树节点数量⼩于6时,会将红⿊树重新变为单链表。

这种方式也是为了提高检索效率,二叉树的检索会再次缩小扫描范围。提高效率

put() 方法原理

  1. 先将 key, value 封装到 Node 对象中
  2. 底层会调⽤ key 的 hashCode() ⽅法得出 hash 值
  3. 通过哈希函数/哈希算法,将 hash 值转换为数组的下标
  • 如果下标位置上没有任何元素,就把 Node 添加到这个位置上;
  • 如果下标位置上有但链表,此时会将当前 Node 中的 key 与链表上每⼀个节点中的 key 进⾏ equals ⽐较
  • 如果所有的 equals ⽅法返回都是 false,那么这个新节点 Node 将被添加到链表的末尾;
  • 如果其中有⼀个 equals 返回了 true,那么链表中对应的这个节点的 value 将会被新节点 Node 的value 覆盖。(保证了不可重复)

注:

  1. HashMap 中允许 key 和 value 为 null,但是只能有⼀个(不可重复)!
  2. HashTable 中 key 和 value 都不允许为 null。 (线程安全)

get() 方法原理

  1. 先调⽤ key 的 hashCode() ⽅法得出 hash 值
  2. 通过哈希函数/哈希算法,将 hash 值转换为数组的下标
  3. 通过数组下标快速定位到数组中的某个位置:
  • 如果这个位置上什么也没有(没有链表),则返回 null;
  • 如果这个位置上有单链表,此时会将当前 Node 中的 key 与链表上每⼀个节点中的 key 进⾏ equals ⽐较。
  • 如果所有的 equals ⽅法返回都是 false,那么 get ⽅法返回 null;
  • 如果其中有⼀个 equals 返回了 true,那么这个节点的 value 便是我们要找的 value,此时 get ⽅法最终返回这个要找的 value。

放在 HashMap 中 key 的元素(或者放在 HashSet 中的元素)需要同时重写 hashCode() 和 equals() ⽅法!!!

同时重写 hashCode() 和 equals() ⽅法

重写 hashCode() ⽅法时要达到散列分布均匀!!!
如果 hashCode() ⽅法返回⼀个固定的值,那么 HashMap 底层则变成了⼀个单链表;
如果 hashCode() ⽅法所有返回的值都不同,此时 HashMap 底层则变成了⼀个数组。
这两种情况称之为,散列分布不均匀
equals 和 hashCode⽅法⼀定要同时重写

TreeMap

  1. TreeSet/TreeMap 是⾃平衡⼆叉树
  2. TreeSet/TreeMap 迭代器采⽤的是中序遍历⽅式 (有序)
    ⽆序,不可重复,但是可排序

**TreeSet/TreeMap中key 可以⾃动对 String 类型或8⼤基本类型的包装类型进⾏排序 **

TreeSet ⽆法直接对⾃定义类型进⾏排序
直接将⾃定义类型添加到 TreeSet/TreeMap中key 会报错 java.lang.ClassCastException

原因:是因为⾃定义没有实现 java.lang.Comparable 接⼝(此时,使⽤的是 TreeSet 的⽆参构造器)

对 TreeSet/TreeMap 中 key部分 元素,必须要指定排序规则。

主要有两种解决⽅案:

⽅法⼀: 放在集合中的⾃定义类型实现 java.lang.Comparable 接⼝,并重写 compareTo ⽅法
⽅法⼆: 选择 TreeSet/TreeMap 带⽐较器参数的构造器 ,并从写⽐较器中的 compare ⽅法

Properties

Properties 的 key 和 values 都是 String

posted @ 2022-02-21 20:10  一刹流云散  阅读(30)  评论(0编辑  收藏  举报