集合
1、什么是集合
- 概念:对象的容器,定义了对多个对象进行操作的常用方法。可实现数组的功能。
- 和数组区别:
- (1)数组长度固定,集合长度不固定
- (2)数组可以存储基本类型和引用类型,集合只能存储引用类型
- 位置:java.util.*;
2、Collection体系集合
2.1 Collection父接口
-
特点:代表一组任意类型的对象,无序,无下标,不能重复。
-
方法:
-
boolean add(Object obj) //添加一个对象
-
boolean addAll(Collection c) //将一个集合中的所有对象添加到此集合中。
-
void clear() //清空此集合中的所有对象
-
boolean contains(Object o ) //检查此集合中是否包含o对象
-
Boolean equals(Object o)//比较此集合是否与指定对象相等
-
boolean isEmpty()//判断此集合是否为空
-
boolean remove(Object o)//在此集合中移除o对象
-
int size()//返回此集合中的元素个数。
-
Object[] toArray()//将此集合转换成数组
-
2.1.1 List子接口
-
特点:有序、有下标、元素可以重复
-
方法:
- void add(int index, Object o) //在index 位置插入对象o
- boolean addAll(int index,Collection c) //将一个集合中的元素添加到此集合中的index位置
- Object get(int index) //返回集合中指定位置的元素
- Object get(int index) //返回集合中指定位置的元素
- List subList(int fromIndex, int toIndex) //返回fromIndex和toIndex之间的集合元素
2.1.2 List实现类
- ArrayList**[重点]**:
-
数组结构实现,查询快、增删慢;
-
JDK1.2版本,运行效率快、线程不安全。
-
源码分析:
- ArrayList()构造方法
private static final int DEFAULT_CAPACITY = 10; //默认容量是10 public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } //注意:如果没有向集合中添加任何元素时,默认容量是0;添加一个元素之后,容量变成10; 超过10之后,每次扩容大小是原来的1.5倍。 transient Object[] elementData; // non-private to simplify nested class access //存放元素的数组elementData private int size; //实际元素个数
2.add方法
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!! 扩容
elementData[size++] = e;
return true;
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);// DEFAULT_CAPACITY 为10
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); //10+(10\2),右移一位是除于2,等于每次扩容大小是原来的1.5倍
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
- Vector(被替代掉的):
-
数组结构实现,查询快、增删慢;
-
JDK1.0版本,运行效率慢、线程安全。
- LinkedList:
- 链表结构实现,增删快,查询慢。
- 源码分析
transient int size = 0; //集合的大小
transient Node<E> first;//链表的头节点
transient Node<E> last;//链表的尾节点
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
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;
}
}
- ArrayList与LinkedList不同结构实现方式
2.1.3 Set子接口以及Set实现类
-
特点:无序、无下标、元素不可重复
-
方法:全部继承来自Collection的方法。
-
HashSet[重点]:
- 基于HashCode实现元素不重复
- 当存入元素的哈希码相同时,会调用equals进行确认,如结果为true,则拒绝后者存入。
-
TreeSet:
- 基于排列顺序实现元素不重复。
- 实现了SortedSet接口,对集合元素自动排序。
- 元素对象的类型必须实现Comparable接口,指定排序规则。
- 通过CompareTo方法确定是否为重复元素
-
LinkHashSet
2.2 Collection 工具类
- 概念:集合工具类,定义了除了存取以外的集合常用方法
- 方法:
- public static void reverse(List<?> list) //反转集合中元素的顺序
- public static void shuffle(List<?> list) //随机重置集合元素的顺序
- public static void sort(List list) //升序排序(元素类型必须实现Comparable接口)
3、泛型
-
Java泛型是JDK1.5中引入的一个新特性,其本质是参数化类型,把类型作为参数传递。
-
常见形式有泛型类、泛型接口、泛型方法。
-
语法:
<T,…> T 称为类型占位符,表示一种引用类型
-
好处
- 提高代码的重用性
- 防止类型转换异常,提高代码的安全性
4、泛型集合
-
概念:参数化类型、类型安全的集合,强制集合元素的类型必须一致。
-
特点:
- 编译时即可检查,而非运行时抛出异常
- 访问时,不必类型转换(拆箱)。
- 不同泛型之间引用不能相互赋值,泛型不存在多态。
5、Map体系集合
5.1 Map接口的特点
- 用于存储任意键值对(Key-Value)
- 键:无序、无下标、不允许重复(唯一)
- 值:无序、无下标、允许重复
5.2 Map父接口
- 特点:存储一对数据(Key-Value),无序、无下标,键不可重复,值可重复
- 方法:
- V put(K key, V value) //将对象存入到集合中,关联键值。key重复则覆盖原值。
- Object get(Object key) //根据键获取对应的值。
- Set //返回所有key
- Collection values() //返回包含所有值的Collection集合。
- Set<Map.Entry<K,V>> //键值匹配的set集合
5.3 Map的实现类
5.3.1 HashMap [重点]:
- JDK1.2版本,线程不安全,运行效率快;允许用null 作为key或是value。
- 源码分析:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 //hashMap初始容量大小
static final int MAXIMUM_CAPACITY = 1 << 30; //hashMap的数组最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;// 默认加载因子
static final int TREEIFY_THRESHOLD = 8;//jdk1.8 当链表长度大于8时,调整成红黑树
static final int UNTREEIFY_THRESHOLD = 6; //jdk1.8 当链表长度小于6时,调整成链表
static final int MIN_TREEIFY_CAPACITY = 64;// jdk1.8 当链表长度大于8时,并且集合元素个数大于等于64时,调整成红黑树
transient Node<K,V>[] table;// 哈希表中的数组
size; //元素个数
2.无参构造:
public HashMap() {
this.loadFactor = DEFAULT_LOAD_FACTOR; // all other fields defaulted
}
3.put方法
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
4.putVal方法
/**
* Implements Map.put and related methods.
*
* @param hash hash for key
* @param key the key
* @param value the value to put
* @param onlyIfAbsent if true, don't change existing value
* @param evict if false, the table is in creation mode.
* @return previous value, or null if none
*/
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
if (++size > threshold)
resize();
afterNodeInsertion(evict);
return null;
}
- 总结:
- HashMap刚创建时,table是null,为了节省空间,当添加第一个元素时,table的容量调整为16
- 当元素个数大于阈值(16*0.75=12)时,会进行扩容,扩容后大小为原来的2倍。目的是减少调整元素的个数。
- jdk1.8 当每个链表长度大于8,并且数组元素个数大于等于64时,会调整为红黑树,目的提高执行效率
- jdk1.8 当链表长度小于6时,会调整成链表
- jdk1.8 以前,链表时头插入,jdk1.8以后时是尾插入
5.3.2 Hashtable
- JDK1.0 版本,线程安全,运行效率慢;不允许null作为key或是value.
5.3.3 Properties
- Hashtable的子类,要求key和value都是String。通常用于配置文件的读取。
5.3.4 TreeMap
- 实现了SortedMap接口(是Map的子接口),可以对key自动排序。
6 Properties
- Properties:属性集合
- 特点
- 存储属性名和属性值
- 属性名和属性值都是字符串类型
- 没有泛型
- 和流有关