Java集合
一、Colection接口
Collection接口:单列集合,用来存储一个一个的对象
1.1 List接口
List接口:存储有序的、可重复的数据(“动态”数组)
- 常用方法
void add(int index, E element)//添加
boolean addAll(int index, Collection<? extends E> c)//添加另一个集合中的元素
E get(int index)//查找
int indexOf(Object o)//通过下标查找
ListIterator<E> listIterator()//迭代器
ListIterator<E> listIterator(int index)
E remove(int index)//删除某下标的数据
E set(int index, E element)//修改数据
List<E> subList(int fromIndex, int toIndex)//求子串
1.1.1 ArrayList
List接口的主要实现类
ArrayList arrayList = new ArrayList();
- jdk1.6:
public ArrayList() {
this(10);
}
底层创建的是长度为10的Object[ ]数组elementData
- jdk1.7:
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private transient Object[] elementData;
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA;
}
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
无参构造创建的是一个空的数组,在add方法中初始化的默认值为10
- 扩容
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//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);//复制
}
每次扩容长度为之前的1.5倍,同时将原数组中的数据复制到新的数组
- 结论:使用时经历避免使用空参构造器
- jdk1.8
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
1.1.2 LinkedList
LinkedList linkedList = new LinkedList();
其底层是双向链表,存储单元为Node
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;
}
}
1.1.3 Vector
List接口的古老实现类(JDK1.0),不太常用
它底层和Arraylist一样,是通过数组实现的,但是它支持线程的同步
默认扩容为2倍
public Vector() {
this(10);
}
1.1.4 三者的异同
- ArrayList:
- 线程不安全,效率高
- 底层使用Object[ ] elementData存储
- LinkedList:
- 底层使用双向链表存储
- 对于频繁的插入、删除操作,效率较高
- Vector:
- 线程安全,效率低
- 底层使用Object[ ] elementData存储
- 相同处:
- 三个类都是实现了List接口,存储数据的特点相同:存储有序的、可重复的数据
1.2 Set接口
存储无序的、不可重复的数据
- 无序性:数据在底层数组中并非按照数组索引的顺序添加,而是由hashcode决定数组的索引
- 不可重复性:通过hashcode()和equals()判断是否重复,先判断hashcode()是否相等,相等就再判断equals()
1.2.1 HashSet
Set接口的主要实现类,线程不安全,可以存储null值,实际上底层是用hashmap实现的
public HashSet() {
map = new HashMap<>();
}
1.2.2 LinkedHashSet
作为HashSet的子类;遍历其内部数据时,可以按照添加的顺序遍历
在HashSet的基础上添加了两个索引指针方便按添加遍历
1.2.3 TreeSet
底层使用的是红黑树存储,可以按照添加对象的指定属性进行排序
- 向TreeSet中添加的数据必须是相同的对象
- 自定义的类型必须有排序的方法,用于比较,而不是使用equals()
1.3 Map接口
- 双列数据,存储key-value对的数据
1.3.1 HashMap
- Map的主要实现类
- 线程不安全
- 效率高
- 可以存储null的key和value
- 底层:
- 数组+链表(jdk7之前)
- 数组+链表+红黑树(jdk8)
- CRUD:
- V put(K key, V value)
- void putAll(Map m)
- V remove(Object key)
- void clear()
- V get(Object key)
- int size()
- boolean containsKey(Object key)
- boolean containsValue(Object value)
- boolean equals(Object o)
- 遍历
- 遍历key集合
//遍历key集合
Set set = hashMap.keySet();
Iterator iterator = set.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
- 遍历value集合
//遍历value集合
Collection values = hashMap.values();
for (Object value : values) {
System.out.println(value);
}
- 遍历所有的key-value
- entrySet()
//entrySet()
Set entrySet = hashMap.entrySet();
Iterator iterator1 = entrySet.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
Map.Entry entry = (Map.Entry) obj;
System.out.println(entry.getKey()+"----->"+entry.getValue());
}
- 遍历key和value本质都是获取entrySet再获取key和value,所以其顺序是对应的
- JDK7
- new HashMap()
- 底层创建了长度为16的一维Entry[ ] table
- map.put(key1,value1)
- 首先,调用key1所在类的hashcode() 计算key1的哈希值,以此得到在Entry数组中的位置。如果位置为空,则添加成功。如果不为空(此位置上有一个或者多个数据(链表)),则比较两者的哈希值:
- 如果key1的哈希值与已经存在的数据的哈希值不同,则key1-value1添加成功------情况2
- 如果key1的哈希值与已经存在的某一个数据(key2-value2)的哈希值相同,则继续比较:调用key1所在类的euqals(key2)
- 如果equals()返回false:此时key1-value1添加成功------情况3
- 如果equals()返回true:使用value1覆盖value2
- 情况2和情况3时key1-value1和原来的数据以链表的形式存储
- 在添加的过程中,默认扩容为原来的2倍,并将原有的数据复制过来
- 首先,调用key1所在类的hashcode() 计算key1的哈希值,以此得到在Entry数组中的位置。如果位置为空,则添加成功。如果不为空(此位置上有一个或者多个数据(链表)),则比较两者的哈希值:
- new HashMap()
- JDK8
- new HashMap():底层没有创建长度为16的数组
- JDK8底层的数组是Node[ ],而非Entry[ ]
- 首次调用put()方法时,底层创建长度为16的数组
- JDK7底层只有:数组+链表。JDK8底层结构:数组+链表+红黑树。
- 当数组的某一个索引位置上的元素以链表的形式存在的数据个数 > 8,且当前数组的长度 > 64时,此索引位置上的所以数据改为使用红黑树存储(二叉树能提高查找效率)
- 负载因子:
DEFAULT_LOAD_FACTOR = 0.75f;
threshold = loadFactor * 容量,当已存入的数据个数>threshold时扩容
1.3.2 ConcurrentHashMap
1.3.3 LinkedHashMap
- 在HashMap的基础上添加了前后指针,能够按照添加的顺序遍历
- 对于频繁的遍历操作,执行效率高于HashMap
static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;//双向链表
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}
1.3.4 TreeMap
- 保证按照添加的key-value对进行排序,实现排序遍历
- 底层使用红黑树
- 自然排序
- 定制排序
1.3.5 Hashtable
- 古老的实现类
- 线程安全,效率低
- 不能存储null的key和value
1.3.6 Properties
- Hashtable的子类,常用于处理配置文件,key、value都是String
- 常配合文件流读取配置文件
1.3.7 Collections工具类
- Collections是操作Collection、Map的工具类
- Reverse(List):逆序排序
- Shuffle(List):打乱顺序
- Sort(List):排序
- Swap(List,int,int):交换