java集合常用方法及底层数据结构总结

java集合

Collection和Map

Java的集合类主要由两个接口派生而出:Collection和Map,Collection和Map是Java集合框架的根接口,这两个接口又包含了一些子接口或实现类。


Collection接口是List、Set和Queue接口的父接口,该接口里定义的方法既可用于操作Set集合,也可用于操作List和Queue集合。Collection接口里定义了如下操作集合元素的方法。

  • boolean add(Object o):该方法用于向集合里添加一个元素。如果集合对象被添加操作改变了,则返回true。

  • boolean remove(Object o):删除集合中的指定元素o,当集合中包含了一个或多个元素o时,这些元素将被删除,该方法将返回true。

  • boolean contains(Object o):返回集合里是否包含指定元素。

  • void clear():清除集合里的所有元素,将集合长度变为0。

  • boolean isEmpty():返回集合是否为空。当集合长度为0时返回true,否则返回false。

  • Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素。

  • Object[] toArray():该方法把集合转换成一个数组,所有的集合元素变成对应的数组元素。

    如果想依次访问集合里的每一个元素,则需要使用某种方式来遍历集合元素,下面介绍遍历集合元素的两种方法。
    1,使用Iterator接口遍历集合元素
    
    - boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true。
    - Object next():返回集合里的下一个元素。
    - void remove():删除集合里上一次next方法返回的元素。
    
    2,使用foreach循环遍历集合元素
    
1. Collection
1.1 Set

Set集合与Collection基本上完全一样,它没有提供任何额外的方法。实际上Set就是Collection,只是行为略有不同(Set不允许包含重复元素)。

Set判断两个对象相同不是使用==运算符,而是根据equals方法。

各Set实现类的性能分析:

HashSet的性能总是比TreeSet好(特别是最常用的添加、查询元素等操作),因为TreeSet需要额外的红黑树算法来维护集合元素的次序。只有当需要一个保持排序的Set时,才应该使用TreeSet,否则都应该使用HashSet。

HashSet还有一个子类:LinkedHashSet,对于普通的插入、删除操作,LinkedHashSet比HashSet要略微慢一点,这是由维护链表所带来的额外开销造成的;不过,因为有了链表,遍历LinkedHashSet会更快。

EnumSet是所有Set实现类中性能最好的,但它只能保存同一个枚举类的枚举值作为集合元素。

必须指出的是,Set的三个实现类HashSet、TreeSet和EnumSet都是线程不安全的。如果有多个线程同时访问一个Set集合,并且有超过一个线程修改了该Set集合,则必须手动保证该Set集合的同步性。通常可以通过Collections工具类的synchronizedSortedSet方法来“包装”该Set集合。此操作最好在创建时进行,以防止对Set集合的意外非同步访问。


(1)HashSet类

创建HashSet时,实际上new了一个HashMap,底层调用的是HashMap中的putVal()方法

public HashSet() {
        map = new HashMap<>();
}
public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}
//hash(key) 计算出传进来元素的哈希值
static final int hash(Object key) {
        int h;
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); //80121
}

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {}
1.存入的元素和以前该位置的元素的哈希值进行比较
2.如果哈希值不同,继续向下执行,把元素添加集合中
3.如果哈希值一,会调用对象的equals()方法进行比较
4.如果比较equals()方法返回的是false,会继续向下执行,然后将元素插入到集合中
5.如果比较equals()方法返回的是true,说明元素的哈希值和内容都一样,表示元素重复了
其实也就是不赋值

1、HashSet,无序,底层是一个哈希表,JDK8之前是数组+单链表,JDK8之后是数组+单链表/数组+红黑树(为了提高查询效率)

2、LinkedHashSet,属于HashSet子类 有序,底层是哈希表加链表(哈希表就是数组+链表),JDK8之前是数组+单链表+单链表,JDK8之后是数组+单链表+单链表/数组+红黑树+单链表

大多数时候使用Set集合时就是使用HashSet这个实现类。HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。

HashSet集合判断两个元素相等的标准是两个对象通过equals()方法比较相等,并且两个对象的hashCode()方法返回值也相等。

HashSet具有以下特点。

  • 不能保证元素的排列顺序,顺序有可能发生变化。
  • HashSet不是同步的,如果多个线程同时访问一个HashSet,假设有两个或者两个以上线程同时修改了HashSet集合时,则必须通过代码来保证其同步。
  • 集合元素值可以是null。

(2)TreeSet类

创建TreeSet时,实际上new了一个TreeMap,通过查看TreeMap的put源码发现底层数据结构为二叉树。

 public TreeSet() {
        this(new TreeMap<E,Object>());
}

TreeSet是SortedSet接口的实现类,正如SortedSet名字所暗示的,TreeSet可以确保集合元素处于排序状态。

与HashSet集合相比,TreeSet还提供了如下几个额外的方法。

因为TreeSet中的元素是有序的,所以增加了访问第一个、前一个、后一个、最后一个元素的方法,并提供了三个从TreeSet中截取子TreeSet的方法。

[-1 , 2 , 3 , 6 , 10]

  • Object first():返回集合中的第一个元素。 -9

  • Object last():返回集合中的最后一个元素。 10

  • Object lower(Object e):返回集合中位于指定元素之前的元素(即小于指定元素的最大元素,参考元素不需要是TreeSet集合里的元素)。

  • Object higher (Object e):返回集合中位于指定元素之后的元素(即大于指定元素的最小元素,参考元素不需要是TreeSet集合里的元素)。

  • SortedSet subSet(fromElement, toElement):返回此Set的子集合,范围从fromElement(包含)到toElement(不包含)。

    subSet(-1 ,5) —> [2 , 3]

  • SortedSet headSet(toElement):返回此Set的子集,由小于toElement的元素组成。 headSet(3) —> [-1 , 2]

  • SortedSet tailSet(fromElement):返回此Set的子集,由大于或等于fromElement的元素组成。 tailSet(3) —> [3 , 6 , 10]

  • Comparator comparator():如果TreeSet采用了定制排序,则该方法返回定制排序所使用的Comparator;如果TreeSet采用了自然排序,则返回null。自然排序,定制排序 《疯狂java讲义》

(3)EnumSet类

1.2 List

List集合代表一个元素有序可重复的集合,集合中每个元素都有其对应的顺序索引

List作为Collection接口的子接口,可以使用Collection接口里的全部方法。而且由于List是有序集合,因此List集合里增加了一些根据索引来操作集合元素的方法。


  • void add(int index, Object element):将元素element插入到List集合的index处。
  • Object get(int index):返回集合index索引处的元素。
  • Object remove(int index):删除并返回index索引处的元素。
  • Object set(int index, Object element):将index索引处的元素替换成element对象,返回新元素。
  • boolean addAll(int index, Collection c):将集合c所包含的所有元素都插入到List集合的index处。
  • int indexOf(Object o):返回对象o在List集合中第一次出现的位置索引。
  • int lastIndexOf(Object o):返回对象o在List集合中最后一次出现的位置索引。
  • List subList(int fromIndex, int toIndex):返回从索引fromIndex(包含)到索引toIndex(不包含)处所有集合元素组成的子集合。

(1)ArrayList

ArrayList和Vector作为List类的两个典型实现,完全支持前面介绍的List接口的全部功能。

ArrayList和Vector类都是基于数组实现的List类

1、ArrayList 底层是数组

2、LinkedList 底层是双向链表

数组:查询快,增删慢

链表:增删快,查询慢

(2)LinkedList

List 接口的链接列表实现。(方法详见JDK API 手册)

实现所有可选的列表操作,并且允许所有元素(包括 null)。除了实现 List 接口外,LinkedList 类还为在列表的开头及结尾 getremoveinsert 元素提供了统一的命名方法。这些操作允许将链接列表用作堆栈、队列双端队列

(3)Vector

底层是数组,线程安全,效率低

1.3 Queue

Queue用于模拟队列这种数据结构,队列通常是指“先进先出”(FIFO)的容器。通常,队列不允许随机访问队列中的元素。

Queue接口有一个PriorityQueue实现类。

除此之外,Queue还有一个Deque接口,

Deque代表一个“双端队列”,双端队列可以同时从两端来添加、删除元素,因此Deque的实现类既可当成队列使用,也可当成栈使用。

Java为Deque提供了ArrayDeque和LinkedList两个实现类。


Queue接口中定义了如下几个方法:

  • void add(Object e):将指定元素加入此队列的尾部。

  • boolean offer(Object e):将指定元素加入此队列的尾部。当使用有容量限制的队列时,此方法通常比add(Object e)方法更好。

  • Object element():获取队列头部的元素,但是不删除该元素。

  • Object peek():获取队列头部的元素,但是不删除该元素。如果此队列为空,则返回null。

  • Object remove():获取队列头部的元素,并删除该元素。

  • Object poll():获取队列头部的元素,并删除该元素。如果此队列为空,则返回null。


(1)PriorityQueue

PriorityQueue是一个比较标准的队列实现类。**PriorityQueue保存队列元素的顺序并不是按加入队列的顺序,而是按队列元素的大小进行重新排序。**因此当调用peek()方法或者poll()方法取出队列中的元素时,并不是取出最先进入队列的元素,而是取出队列中最小的元素

PriorityQueue不允许插入null元素,它还需要对队列元素进行排序,PriorityQueue的元素有两种排序方式。

**自然排序:**采用自然顺序的PriorityQueue集合中的元素必须实现了Comparable接口,而且应该是同一个类的多个实例,否则可能导致ClassCastException异常。

**定制排序:**创建PriorityQueue队列时,传入一个Comparator对象,该对象负责对队列中的所有元素进行排序。采用定制排序时不要求队列元素实现Comparable接口。

PriorityQueue队列对元素的要求与TreeSet对元素的要求基本一致


(2)Deque接口与ArrayDeque实现类

Deque接口是Queue接口的子接口,它代表一个双端队列,Deque接口里定义了一些双端队列的方法,这些方法允许从两端来操作队列的元素。详细方法见JDK API

  • void addFirst(Object e):将指定元素插入该双端队列的开头。
  • boolean offerFirst(Object e):将指定元素插入该双端队列的开头。
  • void addLast(Object e):将指定元素插入该双端队列的末尾。
  • boolean offerLast(Object e):将指定元素插入该双端队列的末尾。
  • Object getFirst():获取但不删除双端队列的第一个元素
  • Object peekFirst():获取但不删除该双端队列的第一个元素;如果此双端队列为空,则返回null。
  • Object getLast():获取但不删除双端队列的最后一个元素。
  • Object peekLast():获取但不删除该双端队列的最后一个元素;如果此双端队列为空,则返回null。
2. Map

Map用于保存具有映射关系的数据,因此Map集合里保存着两组值,一组值用于保存Map里的key,另外一组值用于保存Map里的value,key和value都可以是任何引用类型的数据。Map的key不允许重复,即同一个Map对象的任何两个key通过equals方法比较总是返回false。

(1)HashMap

1、HashMap,JDK1.8之前,采用数组+链表组成的,在JDK1.8后,当链表长度大于阈值(默认为8),且数组大小超过容量时,会将链表转换为红黑树

  • 和HashSet和LinkedHashSet关系一样,HashMap也有一个直接子类LinkedHsahMap

2、LinkedHsahMap:是实现了Map接口,底层是依赖于哈希表和链表的,具有可预知的遍历顺序

  • 哈希表保证唯一性,保证的是Key的唯一性
  • 链表保证有序,保证的是键的有序(存储和取出顺序一致)
  • LinkedHashMap需要维护元素的插入顺序,因此性能略低于HashMap的性能;
(2)TreeMap

TreeMap就是一个红黑树数据结构

两种排序方式:

  • 自然排序:TreeMap的所有key必须实现Comparable接口,而且所有的key应该是同一个类的对象,否则将会抛出ClassCastException异常。
  • 定制排序:创建TreeMap时,传入一个Comparator对象,该对象负责对TreeMap中的所有key进行排序。采用定制排序时不要求Map的key实现Comparable接口。
3.集合底层数据结构
(1)List:
  • ArrayList:Object数组
  • vector:Object数组
  • LinkedList:双向循环链表
(2)Set:
  • hashSet(无序,唯一):基于hashMap实现,底层采用hashMap实现
  • LinkedHashSet:继承于hashSet,基于hashMap实现
  • TreeSet:红黑树
(3)Map:
  • HashMap:JDK1.8之前,采用数组+链表组成的,在JDK1.8后,当链表长度大于阈值(默认为8),且数组大小超过容量时,会将链表转换为红黑树
  • LinkedHashMap:LinkedHashMap继承自hashMap,所以它底层仍然是基于拉链式散列结构组成
  • hashTable:数组+链表组成的,数组时hashMap的主题,链表主要是为了解决哈希冲突而存在的
  • TreeMap:红黑树

hashMap实现,底层采用hashMap实现

  • LinkedHashSet:继承于hashSet,基于hashMap实现
  • TreeSet:红黑树
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值