Java collection 集合

一、集合框架概述

1.1 概念

集合和数组都是对多个数据进行操作的结构,叫做java容器。

1.2 数组存储数据的特点

  • 一旦初始化之后,其长度就确定了
  • 一旦定义好之后,其数据类型就确定了,比如int[]、String[]

1.3 数组存储元素的弊端

  1. 一旦初始化以后,其长度不可修改
  2. 数组中提供的方法非常有限,对于插入、删除、添加数据等操作非常不便,同时效率不高
  3. 获取数组中实际元素的个数,数组没有现成的方法或属性可用
  4. 数组存储数据的特点是有序的、可重复的,对于无序、不可重复的需求、不能满足

1.4 Java集合

Java集合可分为Collections和Map两种体系。

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

  1. List:元素有序,可重复的集合
  2. set:元素无序,不可重复的集合

Map接口:双列集合,保存具有key-value 的集合

二、Collection 接口方法

方法:

Modifier and Type Method and Description
boolean add(E e) 确保此集合包含指定的元素(可选操作)。
boolean addAll(Collection<? extends E> c) 将指定集合中的所有元素添加到此集合(可选操作)。
void clear() 从此集合中删除所有元素(可选操作)。
boolean contains(Object o) 如果此集合包含指定的元素,则返回 true
boolean containsAll(Collection<?> c) 如果此集合包含指定 集合中的所有元素,则返回true。
boolean equals(Object o) 将指定的对象与此集合进行比较以获得相等性。
int hashCode() 返回此集合的哈希码值。
boolean isEmpty() 如果此集合不包含元素,则返回 true
Iterator<E> iterator() 返回此集合中的元素的迭代器。
default Stream<E> parallelStream() 返回可能并行的 Stream与此集合作为其来源。
boolean remove(Object o) 从该集合中删除指定元素的单个实例(如果存在)(可选操作)。
boolean removeAll(Collection<?> c) 删除指定集合中包含的所有此集合的元素(可选操作)。
default boolean removeIf(Predicate<? super E> filter) 删除满足给定谓词的此集合的所有元素。
boolean retainAll(Collection<?> c) 仅保留此集合中包含在指定集合中的元素(可选操作)。
int size() 返回此集合中的元素数。
default Spliterator<E> spliterator() 创建一个Spliterator在这个集合中的元素。
default Stream<E> stream() 返回以此集合作为源的顺序 Stream
Object[] toArray() 返回一个包含此集合中所有元素的数组。
<T> T[] toArray(T[] a) 返回包含此集合中所有元素的数组; 返回的数组的运行时类型是指定数组的运行时类型。
  1. public class TestCollection {
  2. @Test
  3. public void test(){
  4. // add(E e)
  5. Collection col = new ArrayList();
  6. col.add("AA");
  7. col.add("BB");
  8. col.add(11);
  9. col.add(new Date());
  10. System.out.println(col);
  11. //获取元素个数
  12. System.out.println(col.size());
  13. Collection col1 = new ArrayList();
  14. col.add("CC");
  15. col.add("DD");
  16. col.add(22);
  17. //将集合添加到集合中
  18. col.addAll(col1);
  19. System.out.println(col);
  20. //清空元素
  21. col.clear();
  22. //判断元素是否为空
  23. System.out.println(col.isEmpty());
  24. }
  25. }

三、Iterator迭代器接口

1.1 迭代器概念

在程序开发中,经常需要遍历集合中的所有元素。针对这种需求,JDK专门提供了一个接口java.util.IteratorIterator接口也是Java集合中的一员,但它与CollectionMap接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。

1.2 Iterator方法

Modifier and Type Method and Description
default void forEachRemaining(Consumer<? super E> action) 对每个剩余元素执行给定的操作,直到所有元素都被处理或动作引发异常。
boolean hasNext() 如果迭代具有更多元素,则返回 true
E next() 返回迭代中的下一个元素。
default void remove() 从底层集合中删除此迭代器返回的最后一个元素(可选操作)。

代码演示:

  1. public class TestIterator {
  2. @Test
  3. public void test(){
  4. Collection col = new ArrayList();
  5. col.add("AA");
  6. col.add("BB");
  7. col.add(11);
  8. col.add(new Date());
  9. Iterator iterator = col.iterator();
  10. while (iterator.hasNext()){
  11. System.out.println(iterator.next());
  12. }
  13. }
  14. }

remove:

  1. public class TestIterator {
  2. @Test
  3. public void test(){
  4. Collection col = new ArrayList();
  5. col.add("AA");
  6. col.add("BB");
  7. col.add(11);
  8. col.add(16);
  9. col.add(21);
  10. col.add(new Date());
  11. Iterator iterator = col.iterator();
  12. while (iterator.hasNext()){
  13. Object o = iterator.next();
  14. if("16"==String.valueOf(o)){
  15. iterator.remove();
  16. }
  17. }
  18. Iterator iterator1 = col.iterator();
  19. while (iterator1.hasNext()){
  20. System.out.println(iterator1.next());
  21. }
  22. }
  23. }

1.3 Iterator迭代器的执行原理

错误代码:

  1. Iterator iterator = col.iterator();
  2. //错误方式1
  3. while (iterator.next()!=null){
  4. System.out.println(iterator.next());
  5. }
  6. //错误方式2
  7. while (col.iterator().hasNext()){
  8. System.out.println(col.iterator().next());
  9. }

四、List

1.1 List 接口

存储有序的,可重复的数据,List 除了从 Collection 集合继承的方法外, List 集合里添加了一些根据索引来操作集合元素的 方法 。

  • void add(int index, Object ele) 在 index 位置插入 ele 元素
  • boolean addAll (int index, Collection eles) 从 index 位置开始将 eles 中的所有元素添加进来
  • Object get( int index): 获取指定 index 位置的元素
  • int indexOf (Object obj) 返回 obj 在集合中首次出现的位置
  • int lastIndexOf (Object obj) 返回 obj 在当前集合中末次出现的位置
  • Object remove( int index): 移除指定 index 位置的元素,并返回此元素
  • Object set( int index, Object ele) 设置指定 index 位置的元素为 ele
  • List subList (int fromIndex , int toIndex) 返回从 fromIndex 到 toIndex位置的子集合

1.2 ArrayList

作为list的主要实现类,线程不安全,效率高,底层是使用Object[] elementData实现的。

源码扩容问题:

  1. transient Object[] elementData; //初始化一个空的数组
  2. /**
  3. * Constructs an empty list with an initial capacity of ten. 构建
  4. */
  5. public ArrayList() {
  6. this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  7. }
  8. *******************扩容源码*************************************************
  9. /**
  10. * Appends the specified element to the end of this list.
  11. *
  12. * @param e element to be appended to this list
  13. * @return <tt>true</tt> (as specified by {@link Collection#add})
  14. */
  15. public boolean add(E e) {
  16. ensureCapacityInternal(size + 1); // Increments modCount!! size 为list中元素个数
  17. elementData[size++] = e;
  18. return true;
  19. }
  20. private void ensureCapacityInternal(int minCapacity) {
  21. ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
  22. }
  23. //
  24. private static int calculateCapacity(Object[] elementData, int minCapacity) {
  25. if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { //如果当前数组为空,返回DEFAULT_CAPACITY=10
  26. return Math.max(DEFAULT_CAPACITY, minCapacity);
  27. }
  28. return minCapacity; //当前数组不为空,为size+1
  29. }
  30. private void ensureExplicitCapacity(int minCapacity) {
  31. modCount++;
  32. // overflow-conscious code
  33. if (minCapacity - elementData.length > 0) //未扩容前 elementData.length=10
  34. grow(minCapacity); //--->扩容
  35. }
  36. /**
  37. * Increases the capacity to ensure that it can hold at least the
  38. * number of elements specified by the minimum capacity argument.
  39. *
  40. * @param minCapacity the desired minimum capacity
  41. */
  42. private void grow(int minCapacity) {
  43. // overflow-conscious code
  44. int oldCapacity = elementData.length; //原来数组的长度
  45. int newCapacity = oldCapacity + (oldCapacity >> 1); //向右移位操作,变为原来的1.5倍
  46. if (newCapacity - minCapacity < 0) //如果新的数组容量newCapacity小于传入的参数要求的最小容量minCapacity,那么新的数组容量以传入的容量参数为准。
  47. newCapacity = minCapacity;
  48. if (newCapacity - MAX_ARRAY_SIZE > 0)
  49. newCapacity = hugeCapacity(minCapacity);
  50. // minCapacity is usually close to size, so this is a win:
  51. elementData = Arrays.copyOf(elementData, newCapacity); //copy数组元素到新的扩容后的数组
  52. }

1.3 LinkedList

对于频繁插入和删除,使用此类效率比ArrayList高,底层是使用双向链表的结构实现的。

新增方法:

  • void addFirst (Object obj) 在该列表开头插入指定元素
  • void addLast (Object obj) 将指定元素追加到该列表末尾
  • Object getFirst() 返回此列表中第一个元素
  • Object getLast() 返回此列表中最后一个元素
  • Object removeFirst() 从此列表中删除并返回第一个元素
  • Object removeLast() 从此列表删除并返回最后一个元素

源码:

  1. //存储数据的结构node
  2. private static class Node<E> {
  3. E item; //存储的值
  4. Node<E> next; //下一个值结构
  5. Node<E> prev; //上一个值结构
  6. Node(Node<E> prev, E element, Node<E> next) {
  7. this.item = element;
  8. this.next = next;
  9. this.prev = prev;
  10. }
  11. }
  12. /**
  13. * Pointer to first node.
  14. * Invariant: (first == null && last == null) ||
  15. * (first.prev == null && first.item != null)
  16. */
  17. transient Node<E> first;
  18. /**
  19. * Pointer to last node.
  20. * Invariant: (first == null && last == null) ||
  21. * (last.next == null && last.item != null)
  22. */
  23. transient Node<E> last;
  24. //**************************添加元素**************************************
  25. /**
  26. * Appends the specified element to the end of this list.
  27. *
  28. * <p>This method is equivalent to {@link #addLast}.
  29. *
  30. * @param e element to be appended to this list
  31. * @return {@code true} (as specified by {@link Collection#add})
  32. */
  33. public boolean add(E e) {
  34. linkLast(e);
  35. return true;
  36. }
  37. /**
  38. * Links e as last element.
  39. */
  40. void linkLast(E e) {
  41. final Node<E> l = last; //最后一个元素,如果第一次添加为null
  42. final Node<E> newNode = new Node<>(l, e, null); //添加一个新元素
  43. last = newNode; //这个新的元素结构就是链条的最后一个了
  44. if (l == null) //如果为null,说明第一次添加元素
  45. first = newNode;
  46. else
  47. l.next = newNode; //不是第一次添加,那就是链条的最后了
  48. size++;
  49. modCount++;
  50. }

1.4 Vector

作为list接口的古老实现类,线程安全的,底层是使用Object[] elementData实现的。

实例化为长度为10的数组,扩容为原来的2倍。

五、Set

Set接口:存储无序的、不可重复的数据

1.1 HashSet

  • 作为Set接口的主要实现类,线程不安全的,可以存储null
  • HashSet 按 Hash 算法来存储集合中的元素,因此具有很好 的 存取、查找、删除性能
  • HashSet 集合判断两个元素相等的标准 两个对象通过 hashCode () 方法比较相等,并且两个对象的 equals() 方法返回值也 相等
  • 对于存放在 Set 容器中的对象, 对应的类一定要重写 equals 和 hashCode(Objectobj) 方法,以实现对象相等规则 。即:“相等的对象必须具有相等的散列码 “。

HashSet存储图解:

1.2 LinkedHashSet

作为HashSet的子类,遍历内部数据时,可以按照添加的顺序遍历

LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它 同时使用双向链表 维护元素的次序,这使得元素看起来是以 插入顺序保存 的。

底层结构图解:

1.3 TreeSet

可以按照添加指定对象的属性,进行排序。

  • TreeSet 是 SortedSet 接口的实现类, TreeSet 可以确保集合元素处于排序 状态。

  • TreeSet 底层使用 红黑树 结构存储数据。

  • TreeSet 两种排序方法: 自然排序 和 定制排序 。默认情况下, TreeSet采用自然排序。

排序:

自然排序:

  1. TreeSet 会调用集合元素的 compareTo (Object obj ) 方法来比较元素之间的大小关系,然后将集合元素 按 升序 默认情况 排列。
  2. 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable接口。实现 Comparable 的类必须实现 compareTo (Object obj ) 方法,两个对象即通过compareTo (Object obj ) 方法的返回值来比较 大小 。
  3. 因为只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类 的对象。

定制排序:

  • 要 实现定制排序,需要将实现 Comparator 接口的实例作为形参传递给 TreeSet 的构造器。
  1. @Test
  2. public void test2(){
  3. TreeSet set = new TreeSet(new Comparator() {
  4. public int compare(Object o1, Object o2) {
  5. return ((Person)o1).getAge() - ((Person)o2).getAge();
  6. }
  7. });
  8. set.add(new Person("小明",19));
  9. set.add(new Person("小x",20));
  10. set.add(new Person("小g",34));
  11. set.add(new Person("小b",65));
  12. Iterator iterator = set.iterator();
  13. while (iterator.hasNext()){
  14. System.out.println(iterator.next());
  15. }
  16. }

1.4 理解Set的无序性

无序性:不代表随机性

六、Map

1.1 Map接口继承树

1.2 Map接口概述

Map与Collection并列存在。用于存储具有映射关系的数据:key-value

常用方法:

添加 、 删除、修改操作:

  • Object put(Object key,Object value) :将 指定 key value 添加到 或修改 当前 map 对象中
  • void putAll(Map m): 将 m 中的所有 key value 对存放到当前 map 中
  • Object remove(Object key):移除指定 key 的 key value 对,并返回 value
  • void clear():清空当前 map 中的所有数据

元素 查询的操作:

  • Object get(Object key) :获取指定 key 对应的 value
  • boolean containsKey(Object key) :是否包含指定的 key
  • boolean containsValue(Object value):是否包含指定的 value
  • int size():返回 map 中 key value 对的个数
  • boolean isEmpty():判断当前 map 是否为空
  • boolean equals(Object obj) :判断当前 map 和参数对象 obj 是否相等

元 视图操作的方法:

  • Set keySet():返回所有 key 构成的 Set 集合

  • Collection values():返回所有 value 构成的 Collection 集合

  • Set entrySet():返回所有 key value 对构成的 Set 集合

1.3 HashMap

HashMap 是 Map 接口 使用频率最高的实现类。

JDK 7及以前版本: HashMap 是数组+链表结构 即为链地址法。

JDK 8版本发布以后: HashMap 是数组+链表+红黑树实现。

HashMap中的内部类:

  1. static class Node<K,V> implements Map.Entry<K,V> {
  2. final int hash;
  3. final K key;
  4. V value;
  5. Node<K,V> next;
  6. Node(int hash, K key, V value, Node<K,V> next) {
  7. this.hash = hash;
  8. this.key = key;
  9. this.value = value;
  10. this.next = next;
  11. }

1.4 LinkedHashMap

  1. LinkedHashMap 是 HashMap 的 子类。
  2. 在 HashMap 存储结构的基础上,使用了一对双向链表来记录添加元素的顺序。
  3. 与 LinkedHashSet 类似 LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key V alue 对的插入顺序一致。

LinkedHashMap中的内部类: Entry

  1. static class Entry<K,V> extends HashMap.Node<K,V> {
  2. Entry<K,V> before, after;
  3. Entry(int hash, K key, V value, Node<K,V> next) {
  4. super(hash, key, value, next);
  5. }
  6. }

1.5 Properties

Properties 类是 Hashtable 的子类,该对象用于处理属性文件.

  1. Properties pros = new Properties();
  2. pros.load(new FileInputStream("jdbc.properties"));
  3. String user = pros.getProperty("user");
  4. System.out.println(user);

七 、Collections工具类

常用方法:

  • max(Collection<? extends T> coll) 根据其元素的 自然顺序返回给定集合的最大元素。

  • max(Collection<? extends T> coll, Comparator<? super T> comp) 根据指定的比较器引发的顺序返回给定集合的最大元素。

  • reverse(List<?> list) 反转指定列表中元素的顺序

  • sort(List<T> list) 根据其元素的natural ordering对指定的列表进行排序。

  • sort(List<T> list, Comparator<? super T> c) 根据指定的比较器引起的顺序对指定的列表进行排序。

  • copy(List<? super T> dest, List<? extends T> src) 将所有元素从一个列表复制到另一个列表中。

  • frequency(Collection<?> c, Object o) 返回指定集合中与指定对象相等的元素数。