数据集合-实习期笔记

阶段:第一阶段

计划时间:1.06-1.07
实际时间:1.05-1.06
计划内容:

几种常用数据集合的使用方式,数据接口、区别,使用场景等,最好都对每个做分析,并形成笔记输出

  • 数据集合
    • List集合
      • ArrayList
      • LinkedList
    • Map集合
      • TreeMap
      • ConcurrentHashMap
      • HashMap
      • HashTable
    • Queue集合
      • Deque接口
      • ArrayDeque类
    • Set集合
      • HashSet
      • TreeSet
      • LinkedHashSet

笔记内容:

集合接口

img

这里写图片描述

Collection接口

最基本集合接口,一个Collection代表一组Object,即Collection的元素,Java不提供直接继承该接口的类,只提供其子接口

存储一组不唯一,无序的对象。

包含的方法:

Query Operations:

  • int size()
  • isEmpty()
  • contains(Object o)
  • Iterator iterator()
  • boolean isEmpty()
  • boolean add(E e)
  • boolean remove(Object o)
  • containsAll

List接口

有序的Collection,能够准确控制元素插入位置,通过索引访问List中的元素(类似于下标)

存储不唯一,有序(插入)的对象

Set接口

Set 具有与 Collection 完全一样的接口,只是行为上不同,Set 不保存重复的元素。

Set 接口存储一组唯一,无序的对象。

Map接口

Map 接口存储一组键值对象,提供key(键)到value(值)的映射。

SortedMap

继承于 Map,使 Key 保持在升序排列。

区别和选择

  • 容器类仅能持有对象引用(指向对象的指针),而不是将对象信息copy一份至数列某位置。
    一旦将对象置入容器内,便损失了该对象的型别信息。
  • 在各种Lists中,最好的做法是以ArrayList作为默认选择。当插入、删除频繁时,使用LinkedList;
  • Vector总是比ArrayList慢,所以要尽量避免使用。
    在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。
  • HashTree存在的唯一理由:能够维护其内元素的排序状态
    在各种Maps中:HashMap用于快速查找;当元素个数固定,用Array,因为Array效率是最高的。
  • 如果涉及到堆栈,队列等操作,应该考虑用List,对于需要快速插入,删除元素,应该使用LinkedList,如果需要快速随机访问元素,应该使用ArrayList。
  • 如果程序在单线程环境中,或者访问仅仅在一个线程中进行,考虑非同步的类,其效率较高,如果多个线程可能同时操作一个类,应该使用同步的类
  • 要特别注意对哈希表的操作,作为key的对象要正确复写equals和hashCode方法。
  • 尽量返回接口而非实际的类型,如返回List而非ArrayList,这样如果以后需要将ArrayList换成LinkedList时,客户端代码不用改变。这就是针对抽象编程。

List集合

​ List是有序的Collection,使用此接口能够精确的控制每个元素插入的位置。用户能够使用索引(下标)来访问List中的元素,这类似于Java的数组。和Set不同,List允许有相同的元素。
  除了具有Collection接口必备的iterator()方法外,List还提供一个listIterator()方法,返回一个ListIterator接口,和标准的Iterator接口相比,ListIterator允许添加,删除,设定元素,还能向前或向后遍历。

ArrayList

是容量可变的非线程安全集合。内部实现使用数组进行存储,集合扩容时会创建更大的数组空间,把原有数据复制到新数组中。支持对元素的快速随机访问,但是插入与删除时速度通常很慢。ArrayList是List接口的可变数组的实现,底层是数组保存数据的。

特点:

  • 查询快,增删改慢
  • 有序,可重复,可null
  • 线程不安全

实现原理:

  • 扩容时,复制一个新的数组然后在末尾添加一个新的元素;默认初始长度是10 扩容1.5倍+1。

  • 在添加大量元素前,应用程序也可以使用ensureCapacity操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

LinkedList

LinkedList 的本质是双向链表。插入和删除速度更快,但是随机访问速度则很慢。除了继承 AbstractList 抽象类外,LinkedList 还实现了另一个接口 Deque,即 double-ended queue,这个接口同时具有队列和栈的性质。LinkedList 的优点在于可以将零散的内存单元通过附加引用的方式关联起来,形成按链路顺序查找的线性结构,内存利用率较高。

inkedList 是一个双向链表,没有初始化大小,也没有扩容的机制。 里面主要有head表头(包含Entry) 和size长度。

Entry该类是一个实体类、用来表示链表中的一个节点、他包括连接上一个节点的引用、连接下一个节点的引用、和节点的属性。

特点:

  • 查询慢,增删改快
  • 线程不安全
  • 可以被当作堆栈、队列或者双端队列进行操作(通过addFirst(),getFirst(),removeFirst(),removeLast()方法封装进出栈)

备注:

注意LinkedList没有同步方法。如果多个线程同时访问一个List,则必须自己实现访问同步。一种解决方法是在创建List时构造一个同步的List:
    List list = Collections.synchronizedList(new LinkedList(…));

Map集合

Map 集合是以 Key-Value 键值对作为存储元素实现的哈希结构,Key 是唯一的,Value 则是可以重复的。Map的内容可以被当作一组key集合,一组value集合,或者一组key-value映射。所以可以使用 keySet() 查看所有的 Key,使用 value() 查看所有的 Value,使用 entrySet() 查看所有的键值对。在多线程并发场景中,优先推荐使用 ConcurrentHashMap。TreeMap 是 Key 有序的 Map 类集合。

基本操作:

添加

  • put()
  • putAll()

删除

  • remove(“k”) // 根据键删除,不存在返回null
  • remove(“k”,”v”) // 根据具体键值对删除,成功返回true
  • clear() // 删除所有

计数

  • size() // 返回键值对个数

判断

  • isEmpty() // 判断是否为空

取值

  • get(“k”) // 返回指定key对应的value,不存在返回null
  • Conllection set = map.entrySet() // 返回map中键值对组成的Set集合
  • Conllection set = map.keySet() // 返回map中key组成的Set集合
  • Conllection set = map.values() // 返回map中value组成的Set集合

Hashtable

​ Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空的对象都可作为key或者value。
 添加数据使用put(key, value),取出数据使用get(key),这两个基本操作的时间开销为常数。
 Hashtable通过initial capacity和load factor两个参数调整性能。通常缺省的load factor 0.75较好地实现了时间和空间的均衡。增大load factor可以节省空间但相应的查找时间将增大,这会影响像get和put这样的操作。
hashtable是同步的。

TreeMap

TreeSet是Set的一个子类,TreeSet集合是用来对象元素进行排序的,同样他也可以保证元素的唯一。

使用方式(按照指定顺序排序)

  • a.自然顺序(Comparable)
    TreeSet类的add()方法中会把存入的对象提升为Comparable类型
    调用对象的compareTo()方法和集合中的对象比较
    根据compareTo()方法返回的结果进行存储
  • b.比较器顺序(Comparator)
    创建TreeSet的时候可以制定 一个Comparator
    如果传入了Comparator的子类对象, 那么TreeSet就会按照比较器中的顺序排序
    add()方法内部会自动调用Comparator接口中compare()方法排序
    调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
  • c.两种方式的区别
    TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错 ClassCastException)
    TreeSet如果传入Comparator, 就优先按照Comparator

ConcurrentHashMap

ncurrentHashMap是Java中的一个线程安全且高效的HashMap实现。所以其数据结构和内部实现原理,同HashMap大同小异,主要就是通过锁做了线程安全的实现。

.8之前,主要是使用的是Segment数组和分段锁技术实现线程安全处理。其中 Segment 继承于 ReentrantLock。不会像 HashTable 那样不管是 put 还是 get 操作都需要做同步处理。每当一个线程占用锁访问一个 Segment 时,不会影响到其他的 Segment。主要内部结构如下:

img

8之后再锁的实现上做了很大改动,抛弃了原有的 Segment 分段锁,而采用了 CAS + synchronized 来保证并发安全性。也将 1.7 中存放数据的 HashEntry 改为 Node,但作用都是相同的。

HashMap

在JDK1.8之前,HashMap采用数组+链表实现,即使用链表处理冲突,同一hash值的链表都存储在一个链表里。但是当位于一个桶中的元素较多,即hash值相等的元素较多时,通过key值依次查找的效率较低。而JDK1.8中,HashMap采用数组+链表+红黑树实现,当链表长度超过阈值(8)时,将链表转换为红黑树,这样大大减少了查找时间。

特点:

  • key和value都可以为null(key有且只有一个null)
  • 线程不安全

底层原理:

通过hashCode算法,将每个K-V的key值计算得到一个hash值,通过不同的hash值存放每一个键值对;而hash值相同时,则在数组当前节点通过链表,或是红黑树的形式存放hash值相同的键值对。hashMap初始化大小是 16 ,扩容因子默认0.75(可以指定初始化大小,和扩容因子)。

扩容机制:

当前大小 和 当前容量 的比例超过了 扩容因子,就会扩容,扩容后大小为 一倍。例如:初始大小为 16 ,扩容因子 0.75 ,当容量为12的时候,比例已经是0.75 。触发扩容,扩容后的大小为 32。

非线程安全:

当hashmap扩容resize的时候在高并发的情况下reHash有可能会出现死循环(线程不安全,在并发插入元素的时候,有可能出现带环链表,让下一次操作出现死循环)

Queue集合

Queue(队列)是一种先进先出的数据结构,队列是一种特殊的线性表,它只允许在表的一端进行获取操作,在表的另一端进行插入操作。通常,队列不允许随机访问队列中的元素。

基本操作:

失败时抛出异常:

  • add() 在尾部添加
  • remove() 删除并返回头部
  • element() 获取但不删除

失败时返回特殊值(功能依次对应同上):

  • offer() 返回false
  • poll() 返回null
  • peek() 不崩溃,返回null

双端队列:

Deque接口:

增加了针对双端的添加和删除方法。具体在方法名在Queue方法名后加上First/Last,如addFirst()

ArrayDeque类:

ArrayDeque类实现了Deque接口,是一个双端队列。不像LinkedList是一个双向链表,ArrayDeque底层实现是一个循环数组

Set集合

Set 是不允许出现重复元素的集合类型。

​ HashSet 从源码分析是使用 HashMap 来实现的,只是 Value 固定为一个静态对象,使用 Key 保证集合元素的唯一性,但它不保证集合元素的顺序。
​ TreeSet 从源码分析是使用 TreeMap 来实现的,底层为树结构,在添加新元素到集合中时,按照某种比较规则将其插入合适的位置,保证插入后的集合仍然是有序的。

​ 在各种Sets中,HashSet通常优于HashTree(插入、查找)。只有当需要产生一个经过排序的序列,才用TreeSet。

LinkedHashSet 继承自 HashSet,具有 HashSet 的优点,内部使用链表维护了元素插入顺序。

基本操作:

  • boolean add(Object o) :向集合中加入一个对象的引用
  • void clear() :删除集合中所有的对象,即不再持有这些对象的引用
  • boolean isEmpty() :判断集合是否为空
  • boolean contains(Object o): 判断集合中是否持有特定对象的引用
  • Iterartor iterator() : 返回一个Iterator对象,可以用来遍历集合中的元素
  • boolean remove(Object o):从集合中删除一个对象的引用
  • int size() :返回集合中元素的数目
  • Object[] toArray() :返回一个数组,该数组中包括集合中的所有元素

Set遍历:

for (Integer integer :hashSet){
            Log.e(TAG,"set integer :"+integer);
   }

迭代器方式:

Iterator<Integer> iterator =hashSet.iterator();
while (iterator.hasNext()){
     Integer integer=iterator.next();
     Log.e(TAG,"set integer :"+integer);
   }

HashSet

对于HashSet而言,它是基于HashMap实现的,HashSet底层使用HashMap来保存所有元素,因此HashSet 的实现比较简单,相关HashSet的操作,基本上都是直接调用底层HashMap的相关方法来完成。

特点:

  • 存储唯一元素且可null
  • 非线程安全
  • 无序

TreeSet

Map接口另一个重要的实现类TreeMap,TreeMap可以实现元素的自动排序(默认升序)。例如我们常用的签名中,对参数排序就可以用到此类。

TreeMap存储K-V键值对,通过红黑树(R-B tree)实现。

TreeMap的内部结构如下图:

img

LinkedHashSet

Set集合的一个实现,具有Set集合不重复的特点,同时遵循插入的顺序,LinkedHashSet是一个哈希表和链表的结合,是一个双向链表。是HashSet的一个“扩展版本”,HashSet并不管什么顺序,不同的是LinkedHashSet会维护“插入顺序”。HashSet内部使用HashMap对象来存储它的元素,而LinkedHashSet内部使用LinkedHashMap对象来存储和处理它的元素。

特点:

  • 不重复
  • 插入顺序
  • 非线程安全

链图片转存中…(img-iIGqzyxw-1645602327849)]

LinkedHashSet

Set集合的一个实现,具有Set集合不重复的特点,同时遵循插入的顺序,LinkedHashSet是一个哈希表和链表的结合,是一个双向链表。是HashSet的一个“扩展版本”,HashSet并不管什么顺序,不同的是LinkedHashSet会维护“插入顺序”。HashSet内部使用HashMap对象来存储它的元素,而LinkedHashSet内部使用LinkedHashMap对象来存储和处理它的元素。

特点:

  • 不重复
  • 插入顺序
  • 非线程安全
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值