集合概述
- 是一个对象。
- 集合在java中本身是一个容器(来容纳其它类型的数据)。
- 集合中任何时候存储的都是“引用”。
- 集合不能直接存储基本数据类型,另外集合也不能直接存储java对象。例如:list.add(100); //自动装箱Integer,存储该对象的引用地址
- 在java中每一个不同的集合,底层会对应不同的数据结构。
- 往不同的集合中存储元素,等于将数据放到了不同的数据结构当中。
- 所有的集合类和集合接口都在java.util包下。
- 在java中集合分为两大类:
单个方式存储元素 | 键值对的方式存储元素 |
---|---|
单个方式存储元素,这一类集合中超级父接口:java.util.Collection | 以键值对的方式存储元素,这一类集合中超级父接口:java.util.Map |
一、Collection
- java.util.Collection
Collection 是 List 和 Set 的父接口,在 Collection 中定义了一些主要方法
方法名 | 解释 |
---|---|
boolean add(E o) | 确保此 collection 包含指定的元素(可选操作)。 |
boolean addAll(Collection extends E> c) | 将指定 collection 中的所有元素都添加到此collection 中(可选操作)。 |
void clear() | 移除此 collection 中的所有元素(可选操作)。 |
boolean contains(Object o) | 如果此 collection 包含指定的元素,则返回 true。底层调用的是对象的 equals() 方法 |
boolean containsAll(Collection> c) | 如果此 collection 包含指定 collection 中的所有元素,则返回 true。 |
boolean equals(Object o) | 比较此 collection 与指定对象是否相等。 |
int hashCode() | 返回此 collection 的哈希码值。 |
boolean isEmpty() | 如果此 collection 不包含元素,则返回 true。 |
Iterator |
返回在此 collection 的元素上进行迭代的迭代器。 |
boolean remove(Object o) | 从此 collection 中移除指定元素的单个实例,如果存在的话(可选操作)。底层调用了 对象的 equals()方法 |
boolean removeAll(Collection> c) | 移除此 collection 中那些也包含在指定 collection中的所有元素(可选操作)。 |
boolean retainAll(Collection> c) | 仅保留此 collection 中那些也包含在指定collection 的元素(可选操作)。 |
int size() | 返回此 collection 中的元素数。 |
Object[] toArray() | 返回包含此 collection 中所有元素的数组。 |
返回包含此 collection 中所有元素的数组;返回数组的运行时类型与指定数组的运行时类型相同。 |
Iterator 接口
关于 Iterator 接口说明,Iterator 称为迭代接口,通过此接口可以遍历集合中的数据,
- 集合结构改变,必须要重新获取迭代器
- 此接口主要方法为:
方法名 | 解释 |
---|---|
boolean hasNext() | 如果仍有元素可以迭代,则返回 true。 |
E next() | 返回迭代的下一个元素。 |
迭代器迭代元素的过程中不能使用集合对象的remove方法删除元素,
要使用迭代器Iterator的remove方法来删除元素,防止出现异常:
ConcurrentModificationException
- ArrayList类 内部实现 Iterator接口的Itr内部类:源码如下
//获得Iterator的方法
public Iterator iterator() {
return new Itr();
}
//Iterator接口的实现类
private class Itr implements Iterator {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
(一)List接口
是一个有序集合,可以放重复的数据,有下标
List 接口下面主要有两个实现 ArrayList 和 LinkedList,都是有顺序的,也就是放进去是什么顺序,取出来还是什么顺序,也就是基于线性存储,可以看作是一个可变数组
List接口中有一些特有的方法。
方法名 | 作用 |
---|---|
void add(int index, Object element) | 在列表中指定的位置上插入指定的元素(可选操作)。插入位置以及后面的元素后移 |
Object set(int index, Object element) | 用指定元素替换此列表中指定位置的元素(可选操作)。 |
Object get(int index) | 返回此列表中指定位置的元素。 |
int indexOf(Object o) | 返回此列表中指定元素的第一个出现的索引,如果此列表不包含元素,返回- 1。 |
int lastIndexOf(Object o) | 返回此列表中指定元素的最后一个发生的索引,如果此列表不包含元素,返回- 1。 |
Object remove(int index) | 移除此列表中指定位置的元素(可选操作)。 |
1. ArrayList:
查询数据比较快,添加和删除数据比较慢(基于可变数组)
- ArrayList集合初始化容量10(JDK8中的注释:We distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when first element is added.)
- 扩容为原容量 1.5 倍。
- 底层是数组。
- ArrayList集合末尾增删元素效率还是可以的。
2. LinkedList:
查询数据比较慢,添加和删除数据比较快(基于双向链表数据结构)
3. Vector:
Vector 已经不建议使用,Vector 中的方法都是同步的,效率慢,已经被 ArrayList取代
- Vector初始化容量是10.
- 扩容为原容量的 2 倍。
- 底层是数组。
- Vector底层是线程安全的。
- 得到一个线程安全的List: Collections.synchronizedList(list);
4. Stack
是继承 Vector 实现了一个栈,栈结构是后进先出,目前已经被 LinkedList 取代
(二)Set接口
- 是一个无序集合,不允许放重复的数据
1. 哈希表
- 哈希表是一种数据结构,哈希表能够提供快速存取操作。哈希表是基于数组,数组内存放单向链表的
- 缺点,数组一旦创建将不能扩展。
2. HashSet
- 底层是HashMap
- HashSet 中的数据是无序的不可重复的。HashSet 按照哈希算法存取数据的,具有非常好性能
- 它的工作原理是这样的,当向 HashSet 中插入数据的时候,他会调用对象hashCode 得到该对象的哈希码,然后根据哈希码计算出该对象插入到集合中(数组)的位置。
- 重点:特别是向 HashSet 或 HashMap 中加入数据时必须同时覆盖 equals 和 hashCode 方法,应该养成一种习惯覆盖 equals 的同时最好同时覆盖 hashCode
对象间的比较要求
- 两个对象 equals 相等,那么它的 hashcode 相等
- 两个对象 equals 不相等,那么它的 hashcode 并不要求它不相等,但一般建议不相等
- hashcode 相等不代表两个对象相等(采用 equals 比较)
3. TreeSet 实现SortedSet接口
- 底层是TreeMap
- TreeSet 可以对 Set 集合进行排序,默认自然排序(即升序),但也可以做自定义的排序
Comparable 接口
- 放到 TreeSet 中的对象, TreeSet 会对其进行排序,那么该对象必须必须实现 Comparable 接口,基本类型的包装类和 String 他们都是可以排序的,都实现了 Comparable 接口,重写compareTo() 方法,自定义比较规则
Comparable 和 Comparator 的区别
- 一个类实现了 Camparable 接口则表明这个类的对象之间是可以相互比较的,这个类对象组成的集合就可以直接使用 sort 方法排序。
- Comparator 可以看成一种算法的实现,将算法和数据分离(OOP原则),Comparator 也可以在下面两种环境下使用:
1、类的没有考虑到比较问题而没有实现 Comparable,可以通过 Comparator 来实现排序而不必改变对象本身
2、可以使用多种排序标准,比如升序、降序等
二、Map
- java.util.Map;
是一个无序集合,集合中包含一个键对象,一个值对象,键对象不允许重复,值对象可以重复(身份证号—姓名)
- Map 中可以放置键值对,也就是每一个元素都包含键对象和值对象
- Map 实现较常用的为HashMap,HashMap 对键对象的存取和 HashSet 一样
- 采用的是哈希算法,所以如果使用自定类作为 Map 的键对象,必须复写 equals 和hashCode 方法。
- Map接口中常用方法:
方法名 | 作用 |
---|---|
V put(K key, V value) | 向Map集合中添加键值对 |
V get(Object key) | 通过key获取value |
void clear() | 清空map集合 |
boolean containsKey(Object key) | 如果这Map包含一个指定的键映射返回 true |
boolean containsValue(Object value) | 如果映射到指定的值的一个或多个键返回 true |
boolean isEmpty() | 判断Map 集合中元素个数是否为0 |
Set |
获得Map集合所有的key(所有的建是一个set集合) |
V remove(Object key) | 通过key删除键值对 |
int size() | 获取Map集合中键值对的个数 |
Collection |
获取Map集合中所有的value,返回一个Collection |
Set |
将Map集合转换为Set集合 |
-
遍历Map集合的两种方式
- 第一种:获取所有key,遍历每个key,通过key获取value.
- 第二种:获取Set即可,遍历Set集合中的Entry,调用entry.getKey() entry.getValue()
-
put方法和get方法原理
存放的数据结构是哈希表:一个结点数组,数组里面是单向链表
- 可以得出,
- 向Map集合中存,以及从Map集合中取,都是先调用key的hashCode方法,然后再调用equals方法
- key会被调用hashCode方法和equals方法
- 同一个单向链表上所有节点的hash相同,因为是同一个数组小标,但同一个链表上的key之间的equals方法 返回值都是false
- 可以得出,
(一)HashMap类
- 放在HashMap集合key部分的,以及放在HashSet集合中的元素,因为 HashMap 的底层实现采用的是 hash 表,所以 Map 的 key 必须覆盖hashCode 和 equals 方法
- JDK8之后的特点:如果哈希表单向链表中元素超过8个,单向链表这种数据结构会变成红黑树数据结构;当红黑树上的节点数量小于6时,会把红黑树变成单向链表数据结构
(二)Hashtable类
- HashMap和Hashtable的区别
HashMap | Hashtable |
---|---|
初始化容量16,扩容2倍 | 初始化容量11,扩容2倍+1 |
非线程安全 | 线程安全 |
key和value可以为null | key和value都不能是null |
(三)TreeMap类 实现了SortedMap接口
- 底层是二叉树
- treeMap 可以对 Map 中的 key 进行排序,如果 map 中的 key 采用的是自定类那么需要实现Comaprable 或 Comparator 接口完成排序
- 第一种:实现java.lang.Comparable接口。
- 第二种:单独编写一个比较器Comparator接口。
-
自平衡二叉树
(四)Properties类
- 是Map集合,继承Hashtable
- key和value都是string类型
- 常用两个方法
- setProperty
- getProperty
- 被称为属性类对象
- 是线程安全的
三、Collections 工具类
Collections 位于 java.util 包中,提供了一系列实用的方法,如:对集合排序,对集合中的内容查找等
- synchronizedList方法:获得线程安全的集合
- sort方法(要求集合中元素实现Comparable接口。或者传入一个比较器)