集合是提供一种存储空间可变的存储模型,存储容量可随时发生改变
集合类的体系图
其中:Collection、Map、List、Set都是接口,并不能直接实例化对象
ArrayList、LinkedList、HashSet、TreeSet、HashMap……是集合类的实现类
Collection
概述:
是单列集合的顶层接口,表示一组对象,而这些对象也称为Collection的元素
JDK不提供此接口的任何直接实现,它提供更具体的子接口(如List、Set)实现
Collection集合的基本使用
//创建Collection集合的对象
Collection<String> c = new ArrayList<String>();
//添加元素:boolean add(E e)
c.add("hello");
c.add("java");
//输出集合对象
System.out.println(c);
Collection集合的常用方法
方法名 | 说明 |
boolean add(E e) | 把给定的元素添加到当前集合中。 |
boolean remove(Object o) | 从集合中移除指定的元素 |
void clear() | 清空集合中的元素 |
boolean contains(Object o) | 判断集合中是否存在指定的元素 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中元素的个数 |
Collction集合的遍历
迭代器的介绍
迭代器,集合的专用遍历方式
Iterator<E> iterator():返回此集合中元素的迭代器,通过集合的iterator()方法得到
迭代器是通过集合的iterator()方法得到的,所以我们说它是依赖于集合而存在的
iterator中的常用方法
E next():返回迭代器中的下一个元素
Boolean hasNext():如果迭代器中有元素,就返回true
List集合
概述:
有序集合(也称为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素
与Set集合不同,列表通常允许重复的元素
特点:
可以存储重复的元素
有索引
元素存取是有序的
List集合的特有方法
方法名 | 描述 |
void add(int index,E element) | 在此集合中的指定位置插入指定的元素 |
E remove(int index) | 删除指定索引处的元素,返回被删除的元素 |
E set(int index,E element) | 修改指定索引处的元素,返回被修改的元素 |
E get(int index) | 返回指定索引处的元素 |
在使用这些方法时,需注意集合中元素的数量,使用不当程序会报IndexOutOfBoundsException(索引越界异常)
ArrayList
ArrayList的底层是通过数组实现的,所以它的缺点就是每个元素之间都是连续的,当数组空间不足时,要将已有数据的旧数组,复制到扩容好的新数组中。当对ArrayList中间位置进行插入或删除操作时,需要对数组进行大部分的移动。因此它适合查找,不适合增删。
/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;
private void grow(int minCapacity) {
int newCapacity = oldCapacity + (oldCapacity >> 1);
}
从源码中得知它的初始长度为10,扩容机制为:newCapacity = oldCapacity + (oldCapacity >> 1)1.5倍扩容而且它是线程不安全的
Vector
Vector和ArrayList一样也是通过数组实现的,但Vector支持线程同步,所以Vector是线程安全的,但实现同步后它的访问速度就比ArrayList慢。
public Vector() {
this(10);
}
private void grow(int minCapacity) {;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
}
从源码中可看出Vector初始容量当加入第一个元素时,进行第一次扩容时,默认容量大小为 10。,扩容机制为:oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity)两倍扩容
LinkedList
LinkedList的底层是通过链表实现的,所以很适合数据的动态插入和删除,查找和遍历比较慢,另外LinkedList还提供了它特有的方法,针对表头和表尾的操作
方法名 | 说明 |
public void addFirst(E e) | 在该列表开头插入指定的元素 |
public void addLast(E e) | 将指定的元素追加到此列表的末尾 |
public E getFirst() | 返回此列表中的第一个元素 |
public E getLast() | 返回此列表中的最后一个元素 |
public E removeFirst() | 从此列表中删除并返回第一个元素 |
public E removeLast() | 从此列表中删除并返回最后一个元素 |
可以将LinkedList当作数据结构中的队列来使用。
Set集合
特点
元素不可重复
存取是无序的
没有索引,只能通过迭代器或增强for循环遍历
HashSet
HashSet集合的特点:
底层数据结构是哈希表
对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以是不包含重复元素的集合
HashSet集合保证元素唯一性原理
1.根据对象的哈希值计算存储位置
如果当前位置没有元素则直接存入
如果当前位置有元素存在,则进入第二步
2.当前元素的元素和已经存在的元素比较哈希值
如果哈希值不同,则将当前元素进行存储
如果哈希值相同,则进入第三步
3.通过equals()方法比较两个元素的内容
如果内容不相同,则将当前元素进行存储
如果内容相同,则不存储当前元素
LinkedHashSet
LinkedHashSet集合特点
哈希表和链表实现的Set接口,具有可预测的迭代次序
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet
TreeSet集合特点
元素有序,可以按照一定的规则进行排序,具体排序方式取决于构造方法
无参:TreeSet():根据其元素的自然排序进行排序
有参:TreeSet(Comparator comparator) :根据指定的比较器进行排序
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包含重复元素的集合
自然排序Comparable的使用
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序,就是让元素所属的类实现Comparable接口,重写compareTo(T o)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
@Override
public int compareTo(Student s) {
// return 0; //元素相同,不存储
// return 1; //升序
// return -1; //降序
//按照年龄从小到大排序
int num = this.age - s.age;
//年龄相同时,按照姓名的字母顺序排序
//当且仅当e1.compareTo(e2) == 0具有相同的布尔值e1.equals(e2)
int num2 = num==0?this.name.compareTo(s.name):num;
return num2;
}
比较器排序Comparator的使用
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparator的实现类对象,重写compare(T o1,T o2)方法
重写方法时,一定要注意排序规则必须按照要求的主要条件和次要条件来写
//创建集合对象
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
//this.age - s.age
//s1,s2
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
Map集合
Map集合概述和特点
interface Map<K,V> K:键的类型;V:值的类型
Map集合的特点
键值对映射关系
一个键对应一个值
键不能重复,值可以重复
元素存取无序
Map集合的基本功能
方法名 | 说明 |
V put(K key,V value) | 添加元素 |
V remove(Object key) | 根据键删除键值对元素 |
void clear() | 移除所有的键值对元素 |
boolean containsKey(Object key) | 判断集合是否包含指定的键 |
boolean containsValue(Object value) | 判断集合是否包含指定的值 |
boolean isEmpty() | 判断集合是否为空 |
int size() | 集合的长度,也就是集合中键值对的个数 |
Map集合的获取功能
方法名 | 说明 |
V get(Object key) | 根据键获取值 |
Set<K> keySet() | 获取所有键的集合 |
Collection<V> values() | 获取所有值的集合 |
Set<Map.Entry<K,V>> entrySet() | 获取所有键值对对象的集合 |
Map集合的遍历方式
方式1根据键获取对应的值
思路:
存储的元素都是成对出现的,所以把Map看成是一个夫妻对的集合
把所有的丈夫给集中起来
遍历丈夫的集合,获取到每一个丈夫
根据丈夫去找对应的妻子
步骤分析:
获取所有键的集合。用keySet()方法实现
遍历键的集合,获取到每一个键。用增强for实现
根据键去找值。用get(Object key)方法实现
代码实现
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键的集合。用keySet()方法实现
Set<String> keySet = map.keySet();
//遍历键的集合,获取到每一个键。用增强for实现
for (String key : keySet) {
//根据键去找值。用get(Object key)方法实现
String value = map.get(key);
System.out.println(key + "," + value);
}
}
方式二:获取所有的键值对对象
思路:
还是将Map看成是一个夫妻对的集合
这次获取所有结婚证的集合
遍历结婚证的集合,得到每一个结婚证
根据结婚证获取丈夫和妻子
步骤分析
获取所有键值对对象的集合
Set<Map.Entry<K,V>> entrySet():获取所有键值对对象的集合
遍历键值对对象的集合,得到每一个键值对对象
用增强for实现,得到每一个Map.Entry
根据键值对对象获取键和值
用getKey()得到键
用getValue()得到值
代码实现
public static void main(String[] args) {
//创建集合对象
Map<String, String> map = new HashMap<String, String>();
//添加元素
map.put("张无忌", "赵敏");
map.put("郭靖", "黄蓉");
map.put("杨过", "小龙女");
//获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> me : entrySet) {
//根据键值对对象获取键和值
String key = me.getKey();
String value = me.getValue();
System.out.println(key + "," + value);
}
}
HashMap
(jdk1.7以前)底层实现是数据+链表
(jdk1.8之后)底层实现是数组+链表+红黑树
基于哈希表的实现的Map接口。 该实现提供了所有可选的映射操作,并允许null值。( HashMap类大致相当于Hashtable ,除了它是不同步的,并允许null)。
HashMap一个实例有两个影响其性能的参数: 初始容量和负载因子 。
static final float DEFAULT_LOAD_FACTOR = 0.75f;
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16
static final int TREEIFY_THRESHOLD = 8;
static final int UNTREEIFY_THRESHOLD = 6;
final Node<K,V>[] resize() {
Node<K,V>[] oldTab = table;
int oldCap = (oldTab == null) ? 0 : oldTab.length;
int oldThr = threshold;
int newCap, newThr = 0;
if (oldCap > 0) {
if (oldCap >= MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
return oldTab;
}
else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
oldCap >= DEFAULT_INITIAL_CAPACITY)
newThr = oldThr << 1; // double threshold
}
threshold = newThr;
return newTab;
}
当添加某个元素后,数组的总的添加元素数大于了 数组长度 * 0.75(默认),数组长度扩容为两倍。(如开始创建HashMap集合后,数组长度为16,临界值为16 * 0.75 = 12,当加入元素后元素个数超过12,数组长度扩容为32,临界值变为24)
在没有红黑树的条件下,添加元素后数组中某个链表的长度超过了8,数组会扩容为两倍.(如开始创建HashMap集合后,假设添加的元素都在一个链表中,当链表中元素为8时,再在链表中添加一个元素,此时若数组中不存在红黑树,则数组会扩容为两倍变成32,假设此时链表元素排列不变,再在该链表中添加一个元素,数组长度再扩容两倍,变为64,假设此时链表元素排列还是不变,则此时链表中存在10个元素,这是HashMap链表元素数存在的最大值,此时,再加入元素,满足了链表树化的两个条件(1:数组长度达到64, 2:该链表长度达到了8),该链表会转换为红黑树
TreeMap
TreeMap是一个有序的key-value集合,它是通过红黑树实现的。该映射根据其键的自然顺序进行排 序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。
它的构造方法
TreeMap() 使用键的自然顺序构造一个新的、空的树映射。
TreeMap(Comparator comparator) 构造一个新的、空的树映射,该映射根据给定比较器进行排 序。
TreeMap(Map m) 构造一个与给定映射具有相同映射关系的新的树映射,该映射根据其键的自 然顺序 进行排序。
TreeMap(SortedMap m) 构造一个与指定有序映射具有相同映射关系和相同排序顺序的新的树映 射。
LinkedHashMap
继承HashMap中的所有方法,只在Node内部类中,维护一个双向链表,以达到遍历时直接打印双向链表即可,但其他方法比如,get,put等等方法还是用HashMap中的方法。
LinkedHashMap可以保证插入或者访问是有序的
HashTable与HashMap的区别
相同点: 都是实现来Map接口(hashTable还实现了Dictionary 抽象类)。
不同点:
同步性:Hashtable 的方法是 Synchronize 的,线程安全;而 HashMap 是线程不安全的,不是同步的。所以只有一个线程的时候使用hashMap效率要高。
值:HashMap对象的key、value值均可为null。HahTable对象的key、value值均不可为null。
容量:HashMap的初始容量为16,Hashtable初始容量为11,两者的填充因子默认都是0.75。
HashMap扩容时是当前容量翻倍即:capacity * 2,Hashtable扩容时是容量翻倍+1 即:capacity * 2+1。
HashTable不会转换为红黑树
Collections类
区别一下Collection和Colleections
java.util.Collections
public class Collections extends Object
Colleection s 是一个类,一个针对于操作集合的工具类
java.util
Interface Collection<E>
Collection是Java.util包下的一个接口,它也是所有单列集合的顶层接口
Colleections中常用的方法:
方法名 | 说明 |
public static void sort(List<T> list) | 将指定的列表按升序排序 |
public static void reverse(List<?> list) | 反转指定列表中元素的顺序 |
public static void shuffle(List<?> list) | 使用默认的随机源随机排列指定的列表 |