1、集合
总结:集合和数组类似,是一种用于存储元素的可变长容器,
集合与数组的区别:
-
集合的长度可变,数组长度不可变
-
Array能够存储基本数据类型和对象类型,而ArrayList只能存储对象类型
-
Array存储的是同一类型的数据,ArrayList可以存储多种类型,但只能是对象类型
集合的体系:
集合分为单例集合与双例集合,两类的父亲接口下有许多实现类,不同的实现类所用的数据结构与算法都不一样
1.1、集合扩列
List集合的底层实现是数组结构,而数组的大小是不可改变的,当容器的内存不足时,就需要进行扩容,扩容的方法:重新分配一个新的数组,然后复制元素到新的数组中,再将新添加的元素添加到数组末位
-
ArrayList 初始大小:10; 扩容机制:当发现容量不足时扩容,容量为原来的1.5倍+1。
-
LinkedList 初始大小:0; 扩容机制:无。
-
Vector 初始大小:10; 扩容机制:当发现容量不足时,扩容到原来的两倍
-
HashSet 初始大小:16; 扩容机制:加载因子为0.75,当超过这个阈值时扩容,扩容到原来的两倍。它的子链表达到8时 ,转化成红黑树。小于6个时转化成链表。
-
HashMap 初始大小:16; 扩容机制:加载因子为0.75,当超过这个阈值时扩容,扩容到原来的两倍。它的子链表达到8时 ,转化成红黑树。小于6个时转化成链表。
-
Hashtable 初始大小:11; 扩容机制:加载因子为0.75,当超过这个阈值时扩容,扩容为2*原数组长度+1。
1.2、集合的线程安全
-
线程安全
-
vector
-
HashTable
-
-
线程不安全
-
ArrayList
-
LikedList
-
HashMap
-
2、单例集合
2.1、List接口
-
概述:List是一个接口,下面主要有三个个实现类ArrayList和LinkedList和Vector
-
特点:
-
List接口存储的数据为有序排列的,存储的时候是什么顺序,取出来就是什么顺序(1、2、3、4、5)
-
List结构为每一个存储的元素分配了一个索引,通过索引可以精准的访问每一个指定的元素,可以取代数组
-
List接口存储的数据允许重复
-
只有Vector是线程安全的
-
2.1.1、ArrayList集合
特点:因为底层采用数组的数据结构实现,所以查询速度快,增删较慢,需要开辟连续的空间,且线程不安全
常用的方法(由图可知,List是Collection,则List继承Collection中的方法,除此之外还有很多List自身的方法)
-
public boolean add(int index, E element)
: 将指定的元素,添加到该集合中的指定位置上 -
public E get(int index)
:返回集合中指定位置的元素。 -
public object remove(int index)
: 移除列表中指定位置的元素, 返回的是被移除的元素。 -
public E set(int index, E element)
:用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
public class Demo01 {
public static void main(String[] args) {
// 创建List集合对象
List cities = new ArrayList<String>();
cities.add("济南"); // 索引: 0
cities.add("石家庄"); // 索引: 1
cities.add("昆明"); // 索引: 2
System.out.println(cities); // [济南, 石家庄, 昆明]
cities.add(1, "福州"); // 原来的元素往后推
System.out.println(cities); // [济南, 福州, 石家庄, 昆明]
// 删除索引位置为2的元素,返回被删除元素
System.out.println(cities.remove(2)); // 石家庄
System.out.println(cities); // [济南, 福州, 昆明]
// 在指定位置 进行 元素替代(改)
cities.set(0, "银川");
System.out.println(cities); // [银川, 福州, 昆明]
// 获取指定位置元素
System.out.println(cities.get(0)); // 银川
System.out.println("---------");
// 跟size() 方法一起用 来 遍历的
for (int i = 0; i < cities.size(); i++) {
System.out.println(cities.get(i));
}
System.out.println("---------");
//还可以使用增强for
for (Object city : cities) {
System.out.println(city);
}
}
}
2.1.2、LinkedList集合
特点:因为底层采用链表(双向链表)的数据结构实现(亦可以作为栈、队列的结构使用),所以增删速度快,不需要开辟连续的空间,查询速度慢,且线程不安全
常用方法:
-
public void addFirst(E e)
:将指定元素插入此列表的开头。 -
public void addLast(E e)
:将指定元素添加到此列表的结尾。 -
public E getFirst()
:返回此列表的第一个元素。 -
public E getLast()
:返回此列表的最后一个元素。 -
public E removeFirst()
:移除并返回此列表的第一个元素。 -
public E removeLast()
:移除并返回此列表的最后一个元素。 -
public E pop()
:从此列表所表示的堆栈处弹出一个元素。 -
public void push(E e)
:将元素推入此列表所表示的堆栈。 -
public boolean isEmpty()
:如果列表不包含元素,则返回true
public class Demo02 {
public static void main(String[] args) {
LinkedList cities = new LinkedList<String>();
// 添加元素到头部
cities.addFirst("广西"); // cities: [广西]
cities.addFirst("陕西"); // cities: [陕西, 广西]
cities.addFirst("山西"); // cities: [山西, 陕西, 广西]
cities.addFirst("江西"); // cities: [江西, 山西, 陕西, 广西]
System.out.println(cities); // [江西, 山西, 陕西, 广西]
// 获取链表头部元素
System.out.println(cities.getFirst()); // 江西
// 获取链表尾部元素
System.out.println(cities.getLast()); // 广西
// 删除链表头部元素并返回
System.out.println(cities.removeFirst()); // 江西
// 删除链表尾部元素并返回
System.out.println(cities.removeLast()); // 广西
System.out.println(cities); // [山西, 陕西]
}
}
LinkedList与ArrayList:
-
LinkedList数据结构是双向循环链表,ArrayList的数据结构是数组
-
LinkedList不支持随机访问,ArrayList支持随机访问
2.2、Set接口和实现类
特点:无序、无下限、元素不可重复,方法全部来自于父类Collection,没有什么 功能上的扩充
2.2.1、HashSet集合
特点:
-
是Set接口的一个实现类
-
无序,元素不可重复
-
底层是一个HasMap,HashSet的值存放再HashMap的Key上
去重原理:HashSet根据对象的哈希值来确定元素再集合中的存储位置,因此具有良好的存储和查找性能,当存入元素时会调用对象的hashCode()方法计算该对象的hash值,当哈希码相同时(可能为hash冲突), 会调用equals()方法来进行确认(确认的是对象内存的地址值),结果为true则拒绝存入
public class Demo01 {
public static void main(String[] args) {
//创建 Set集合
HashSet set = new HashSet<String>();
//添加元素
set.add(new String("河北"));
set.add("湖北");
set.add("河北");
set.add("湖北");
//遍历
for (Object name : set) {
System.out.println(name);
}
}
}
注意:HashSet在Jdk8以后底层采用了数组+链表+红黑树;当HashSet存储的数据太多,数组的容量达到64且链表长度大于阈值8时,会将数组上的链表转化为红黑树,以降低查询时间
2.2.2、TreeSet集合
与HashSet一样,为不可重复且无序,但TreeSet可以根据一定的排序规则对元素进行排序,需要注意的时,TreeSet存储的元素必须实现Comparable接口(一些类型以及实现了,比如String),否则会抛出异常
2.3、Iterator
概念:Iterator:一种设计模式,是一个对象,可以用来遍历序列中的对象
使用for-each遍历集合序列时,如果要改变序列中的值,要使用iterator,否则会报错;原因是for-each本质是使用iterator进行遍历,iterator在使用next获取下一个元素时,会先检查序列中的值有没有被改变,如果改变就会抛出错误
3、双例集合
特点:
-
存储一堆数据、无序、无下标、键key不可以重复,值value可以
-
用于保存具有映射关系的键值对
-
HashMap对于Key的去重原理采用的是哈希算法,即计算元素的哈希值hashcode(),再判断是否使用equals方法
常用方法:
-
public V put(K key, V value)
: 把指定的键与指定的值添加到Map集合中。 -
public V remove(Object key)
: 把指定的键 所对应的键值对元素 在Map集合中删除,返回被删除元素的值。 -
public V get(Object key)
根据指定的键,在Map集合中获取对应的值。 -
boolean containsKey(Object key)
判断集合中是否包含指定的键。 -
public Set<K> keySet()
: 获取Map集合中所有的键,存储到Set集合中。 -
public Set<Map.Entry<K,V>> entrySet()
: 获取到Map集合中所有的键值对对象的集合(Set集合)。
数据存储实例:
public class Demo01 {
public static void main(String[] args) {
//创建 map对象
HashMap<String, String> map = new HashMap();
//添加元素到集合
map.put("江西", "南昌");
map.put("湖南", "长沙");
map.put("湖北", "武汉");
// 存取是无序的
System.out.println(map); // {湖南=长沙, 湖北=武汉, 江西=南昌}
// String remove(String key): 根据key来删除记录,并将key对应的value返回
System.out.println(map.remove("江西")); // 南昌
System.out.println(map); // {湖南=长沙, 湖北=武汉}
// 查看 湖北的省会 是哪座城市
System.out.println(map.get("湖北")); // 武汉
System.out.println(map.get("湖南")); // 长沙
}
}
数据遍历实例:
使用方法:
-
public Set<K> keySet()
: 获取Map集合中所有的键,存储到Set集合中。 -
public V get(Object key)
根据指定的键,在Map集合中获取对应的值。
public class Demo02 {
public static void main(String[] args) {
Map<String, String> cities = new HashMap<>();
//添加元素到集合
cities.put("广西", "南宁");
cities.put("云南", "昆明");
cities.put("贵州", "贵阳");
// 1.获取key的集合
Set<String> provinces = cities.keySet(); // [贵州, 广西, 云南]
System.out.println(provinces);
// 2.遍历key的集合
for (String province : provinces) {
// 3.根据key(省份)拿到value(省会城市)
String city = cities.get(province);
System.out.println(province + "省的省会是:" + city);
}
}
}