Java集合(一)

几个概念

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

  • List:元素有序,可以重复的集合
  • Set:元素无序,不可重复的集合

2. Map接口:双列数据,保存具有key-value键值对的集合

3. Conllection接口方法

  • retainAll(collection对象):求两个集合交集
  • removeAll(collection对象):求两个集合差集
  • toarry():集合转数据
  • Arrays.aslist(数组):数组转集合
  • iterator():返回一个迭代器对象,仅用于遍历集合,默认游标在第一个元素之前

4. Collections集合方法:均为static方法

  • reverse():反转
  • shuffle():随机排序
  • frequency():返回指定元素出现的次数
  • copy():复制list,复制出来的list大小不可小于被复制的list

5. List接口:存储有序的、可重复的数据。  -->“动态”数组,替换原有的数组

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

6. Map接口:双列数据,存储key-value对的数据 

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

List

1. ArrayList源码

  • JDK7的情况
    • 底层创建了长度是10的Object[]数组elementData
    • 数组容量不够,默认情况下,扩容为原来的容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中
    • 建议开发中使用带参的构造器:ArrayList list = new ArrayList(int capacity)
  • JDK8的情况
    • 底层Object[] elementData初始化为{},并没有创建长度为10的数组
    • 第一次调用add()时,底层才创建了长度10的数组,并将数据添加到elementData[0]
    • 后续的添加和扩容操作与jdk7无异
  • 7与8的对比
    • jdk7中的ArrayList的对象的创建类似于单例的饿汉式
    • jdk8中的ArrayList的对象的创建类似于单例的懒汉式,延迟了数组的创建,节省内存

2. LinkdeList源码

  • 内部声明了Node类型的first和last属性,默认值为null
  • 调用add()时,将数据封装到Node中,创建了Node对象
private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

3. Vector源码

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

4. 调用remover()注意

  • remove(int index):移除指定index位置的元素
  • remove(Object obj):移除指定对象元素

5. ArrayList和LinkedList的异同

  • 二者都线程不安全,相对线程安全的Vector,执行效率高。
  • ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
  • 对于随机访问get和set,ArrayList优于LinkedList,因为LinkedList要移动指针。
  • 对于新增和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据。

6. ArrayList和Vector的区别

  • Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于强同步类。因此开销就比ArrayList要大,访问要慢。
  • 正常情况下,大多数的Java程序员使用ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。
  • Vector每次扩容请求其大小的2倍空间,而ArrayList是1.5倍。
  • Vector还有一个子类Stack。

Set

1. 对于存放在Set容器中的对象

  • 对应的类一定要重写equals()和hashCode(Object obj)方法,以实现对象相等规则
  • 重写的两个方法尽可能保持一致性,相同的对象必须具有相同的散列码
  • TreeSet存储的对象需要实现Comparable接口或者new TreeSet时传入Comparator对象

2. Set的两个特性

  • 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据数据的哈希值决定的。
  • 不可重复性:两个对象通过hashCode()方法比较相等,并且两个对象的equals()方法返回值也相等。即相同的元素只能添加一个。

3. HashSet

  • 不能保证元素的排列顺序
  • 线程不安全的
  • 可以存储null值

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

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

5. 为什么用Eclipse/IDEA复写hashCode方法,有31这个数字

  • 选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,所谓的“冲突”就越少,查找起来效率也会提高。(减少冲突)
  • 并且31只占用5bits,相乘造成数据溢出的概率较小。
  • 31可以由i*31==(i<<5)-1来表示,现在很多虚拟机里面都有做相关优化。(提高算法效率)
  • 31是一个素数,素数作用就是如果我用一个数字来乘以这个素数,那么最终出来的结果只能被素数本身和被乘数还有1来整除!(减少冲突)

6. LinkedHashSet

  • HashSet的子类
  • 由于是双向链表,遍历内部数据时,可以按照添加的顺序访问
  • 其内部顺序还是无序的
  • 对于频繁的遍历操作,LinkedHashSet效率高于HashSet

7. TreeSet

  • 添加的数据,要求是相同类的对象
  • 对象必须实现Comparable接口,或者创建TreeSet传入Comparator对象
  • 底层使用的是红黑树,保证插入数据是按照大小排序
  • 查询速度比list快
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值