Java之集合
一、认识集合
-
集合也是一个对象,集合中不存储基本数据类型,只存储java中对象的内存地址,(基本类型数据存入集合,java后台会进行自动装箱)
-
java中每种不同的集合都对应一个不同的数据结构,不同的数据结构,存储数据的方式不同。数据结构类型有:顺序表,栈,堆,队列,树,哈希表,链表等。。。例如:java中ArrayList类,Vector类底层为数组,LinkedList 底层是链表(双向),TreeMap 底层是二叉树,HashMap类底层是哈希表
-
所有的集合类和集合接口都在java.util.*包下
-
java中集合分为两大类:
一类是单个方式存储元素,其超级父接口是java.util.Collection;
一类是以键值对的方式存储元素,其超级父接口是java.util.Map; -
Java中常用集合的继承结构图
- 总结
ArrayList:底层是数组
LinkedList:底层是双向链表
Vector:底层是数组,线程安全的,效率低,使用少
HashSet底层是HashMap,放到HashSet集合中的元素等同于放到了 HashSet集合的key部分
TreeSet:底层是TreeMap,放到TreeSet集合中的元素等同于放到 TreeMap集合中的key部分,底层根据值的大小自动排序
HashMap:底层是哈希表
Hashtable:底层也是哈希表,只不过是线程安全的,效率低,使用较少
TreeMap底层是二叉树
List集合存储元素的特点:
有序可重复
有序:存进去的顺序和取出来的顺序相同,每一个元素都有下标。
可重复:存进去1,可以再存储一个1
Set/Map(Map中的key)集合存储元素的特点:
无序不可重复
无序:存进去的顺序和取出来的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
SortedSet/SortedMap(SortedMap中的key)集合存储元素特点:
首先是无序不可重复的,但是SortedSet集合中的元素是可排序的。
无序:存进去的顺序和取出来的顺序不一定相同。另外Set集合中元素没有下标。
不可重复:存进去1,不能再存储1了。
可排序:可以按照大小顺序排列
Map集合中的key就是一个Set集合。往Set集合中放数据,实际上是放到了Map集合中的key部分。注意:Map集合中的key不可重复但是value可以重复!!!
二、Collection接口:
1. Collection集合存储哪些元素
不使用“泛型”能存储Object的所有子类型
使用了“泛型”后,只能存储某个具体元素
注意:集合中不能直接存储基本数据类型和java对象,只存储java中对象的内存地址
2. Collection中的常用方法(公共方法,子类中都能用):
boolean add(E e) | 向集合中添加指定的元素 |
---|---|
boolean addAll(Collection<? extends E> c) | 将指定集合中的所有元素添加到这个集合(可选操作)。 |
void clear() | 从这个集合中移除所有的元素(可选操作)。 |
boolean contains(Object o) | 根据equals()方法比较返回 true如果集合包含指定元素。 |
boolean containsAll(Collection<?> c) | 返回 true如果这个集合包含指定集合的所有元素。 |
boolean isEmpty() | 返回 true如果集合不包含任何元素。 |
Iterator iterator() | 返回此集合中的元素的迭代器。 |
boolean remove(Object o) | 根据equals()方法比较,从这个集合中移除指定元素的一个实例,如果它是存在的(可选操作)。 |
boolean removeAll(Collection<?> c) | 删除此集合中包含的所有元素(可选操作)的所有元素(可选操作)。 |
boolean retainAll(Collection<?> c) | 仅保留包含在指定集合中的这个集合中的元素(可选操作)。 |
int size() | 返回此集合中的元素的数目。 |
Object[] toArray() | 返回包含此集合中所有元素的数组。 |
3. 迭代器Iterator
1.通过Collection接口中的iterator()方法获得迭代器对象
2.迭代器对象初始位置指向集合数据的前一个位置
3.迭代器的通用方法
- next()移动迭代器对象使其指向集合的下一个元素并以Object对象返回该元素的值
- hasNext(),判断是否还有下一个元素
- remove(),底层调用equals方法进行比较,存在该对象则删除集合中的该元素
注意:集合结构只要发生变化,迭代器就必须重新获取,否则会出现异常 在使用迭代器进行迭代时,应该使用迭代器中的remove()方法这样就能使得集合中的结构和迭代器指向的结构对应,而不是集合对象的remove()方法,因为集合对象在调用remove()方法时会改变集合中的结构,此时迭代器对象需要重新获取,否则会发生异常
4.Collection中的contains(Object o)方法
Collection中的contains(Object o)判断集合中是否包含集合该对象,底层调用equals方法比较,(存储的类型未重写equals()方法,则默认比较内存地址)(注意:如果是String类,则比较内容,因为String类的equals()方法被重写,比较的是内容),相同返回ture
三、List接口
1.List集合的特点
1.List接口继承Collection接口
2.List集合中的元素有序可重复,即元素有下标,存取顺序一致,可以存储相同的元素
3.集合元素下标从0开始
2.List集合中的常用方法
Collection接口中的方法都可以用
boolean add(int index ,Object element ) | 向指定位置插入新元素 |
---|---|
booleanadd(Object element ) | 向集合末尾中添加新元素 |
int get(int index) | 得到集合中指定位置的元素 |
int indexOf(Object element) | 获取指定对象第一次出现的索引 |
int LastIndexOf(Object element) | 获取指定对象最后一次出现的索引 |
void remove(int index ) | 删除指定位置的元素 |
set(int index ,Object element) | 修改指定位置的元素 |
3.List集合中的常用实现类
1.ArrayList集合
1.ArrayList类的无参构造方法默认容量为10(最初为0,当添加一个元素后初始化容量为10),
2.ArrayList(int initialCapacity) 构造一个长度为initialCapacity的集合
3.ArrayList(Collection c)通过实现了Collection接口的对象创建数组
3.如果add()添加的元素超过初始化容量,则底层自动将容量扩充为原来的1.5倍,扩容效率较低,优化策略,因为底层为数组,所以使用时建议预估计容量创建,
补充:数组扩容方法Arrays.copyOf(array,2*array.length);//第一个参数是待拷贝的数组对象,第二个是新数组的长度
4.底层是Object类数据
5.ArrayList 优点检索效率高
缺点,随机增删效率低(但向末尾添加元素效率高,不受影响
),无法存储大量数据,因为数组很难找到一大片连续的内存空间
2.LinkList集合
LinkList集合底层为双向链表:
优点:随机增删效率高,不涉及大量元素的位移操作
缺点:查找的效率较低,每次查找元素都需要从表头开始遍历
特点:初始化没有任何元素,空间存储上内存地址不连续,适合存储数据量大的数据
3.Vector集合
特点:线程同步安全的,效率较低,底层为数组,无参构造初始化为10个元素,溢出扩容两倍
补充说明:将非线程安全的ArrayList变成线程安全的
(Collections工具类的静态方法sychronizedList(Collection obj)方法将集合obj变成线程安全的)
4.Set集合
Set集合特点:
Set集合是无序不可重复的,放在Set集合中的元素相当于放在了Map集合中的key部分
5.增强for(foreach)
JDK5.0之后推出的特性
语法:for(类型(和被遍历的集合或数组存储的元素类型一致) 变量名:数组或者集合){
}
优点,遍历方便
缺点,没有下标,不能删除集合元素
1、对于数组,foreach 循环实际上还是用的普通的 for 循环
2、对于集合,foreach 循环实际上是用的 iterator 迭代器迭代
四,Map集合
注意:Map和Collection没有关系
1.Map集合中的常用方法
V put(K key ,V value ) | 向Map集合中添加键值对 |
---|---|
V get(Object key) | 通过key获取value |
void clear() | 清空Map集合 |
boolean containsKey(Object key) | 判断Map集合中是否含有某个key |
boolean containsValue(Object value) | 判断Map集合中是否含有某个value |
boolean isEmpty() | 判断Map集合中的元素是否全为空 |
Set(K) keySet() | 获取Map集合中所有的key |
V remove(Object key) | 移除Map集合中key元素的键值对 |
default V replace(K key) | 根据给定的key移除对应的元素 |
int size() | 返回键值对的数目 |
Collection values() | 返回Map集合中所有的value |
Set<Map.Entry<K,V>> entrySet() | 将Map集合转为Set集合 |
ps:containsX()这两个方法底层都是调用equals()方法进行比较,自定义类比较值需要重写,Entry是Map的静态内部类,
2.Map集合中的遍历
1.通过keySet()方法获取所有的key,然后遍历
(注意HashMap的key可以为空,且只有一个,value也可以为空)
2.通过 entrySet()方法转换成存储Map.Entry(本质上是Node节点组成的单链表)元素的Set集合,效率较高,通常使用该方法对Map集合进行遍历
Map集合遍历总结:
1、通过key集合访问,对Key敢兴趣,可以访问与key对应的Value值;
for(String k:maps.keySet()){
System.out.println(k+":"+maps.get(k));
}
2、通过value集合访问,只对value值感兴趣,无法访问key值;
for(String value:maps.values()){
System.out.println(value);
}
3、通过Entry集合访问,对Entry感兴趣,可以访问与key对应的Value值
for(Entry entry:maps.entrySet()){
System.out.println(entry.getKey()+":"+entry.getValue());
}
4、通过迭代Key集合访问Map集合,maps.keySet()返回的是一个Set<String>集合,Set直接继承Collection,所以可以对其进行迭代。
Iterator<String> iterator = maps.keySet().iterator();
while(iterator.hasNext()){
String key = iterator.next();
System.out.println(key+":"+maps.get(key));
}
5、通过迭代Values集合访问Map集合,maps.values()返回的是Collection<String>,所以可以对其迭代。
Iterator<String> iter= maps.values().iterator();
while(iter.hasNext()){
System.out.println(iter.next());
}
6、通过迭代Entry集合访问Map集合,maps.entrySet()返回的是一个Set<Entry<String, String>>,Set直接继承Collection,所以可以对其迭代。
Iterator<Entry<String, String>> it = maps.entrySet().iterator();
while(it.hasNext()){
Entry<String, String> entry = it.next();
System.out.println(entry.getKey()+":"+entry.getValue());
}
3.散列表/哈希表
哈希表是一个数组和单链表的结合体
数组:查询效率高,增删效率低
单向链表:随机增删效率高,查询方面效率低
哈希表将者两种数据结构组合在一起,充分发挥他们各自的优点(数组用于存储链表的表头节点,然后往对应的表头上加元素)
4. put方法添加元素原理:
第一步:将k v 键值对封装到Node对象中,
第二步:底层会调用hashCode()
方法得到k对应的hash值,然后通过哈希算法,将hash值转换为对应数组的下标,下标位置上如果没有任何元素,就把Node添加到这个位置上,如果对应的位置上有链表,此时会拿着k和链表上的每一个节点中的k进行equals()
方法比较,如果都返回false则将该node添加到链表的末尾/或表头,如果有一个equals方法返回true则这个节点将会被覆盖。
5.get方法原理:
先调用k的hashCode()
方法得出哈希值,通过哈希算法转换成数组的下标,通过数组下标快速定位到某个位置上,如果这个下标上面什么都没有,返回null,如果这个下标位置上有(单)链表,则k会与每个单链表上的key值进行equals()
比较,如果所有的equals方法都返回false则get方法会返回null,只要有一个equals方法返回true,则此时这个节点的value就是我们要的value,则get方法就会返回这个value
6.HashMap类
1.放在HashMap集合中的key元素部分和放在HashSet元素部分需要同时重写hashCode()和equals()方法
2.HashMap集合初始化的数组容量是16,默认加载因子是0.75,即存储的数据达到数组容量的75%时会自动扩容为原来的2倍
注意:1.HashMap的初始化容量必须是2的倍数,这是官方推荐,目的是为了哈希表散列分布均匀,提高HashMap的存取效率。2.如果hashCode()方法和equals()方法使用IDEA生产时,需要同时生成
3.JDK8之后对HashMap集合的改进,加了门限值8,即:如果单向链表上的元素个数为8时,会自动把单向链表变成二叉树/红黑树,当元素个数又减小到6时,又会把树变成单向链表
7.Hashtable类
Hashtable的初始化容量为11,默认加载因子也为0.75,扩容方式为原容量的二倍再加1,注意:但是Hashtable的key和value 都不能为空,Hashtable集合是线程安全的
8.properties类
他是Hashtable的子类,它是属性类,它的key和value都是String类,是线程安全的,主要掌握put()和get()方法
9.TreeMap/TreeSet类
特点是无序不可重复,但里面的元素自动排序(数据结构为搜索二叉树),排序需要实现Comparable接口重写compareTo()或传递Comparator接口的比较器对象为集合的排序提供一个比较的依据,对于String和Integer等类型已经实现了Comparable接口的比较功能,我们只有要对我们存储在该集合中的自定义类型实现该接口,并重写compareTo()方法,可以不重写equals()方法。
10、集合比较规则的重写:
1.Comparable接口的compareTo()方法比较规则的重写方式
降序:k1.compareTo(k2),k1比k2大返回大于0的数,k1等于k2返回0,k1比k2小返回小于0的数,即:将列表中的元素作为参数和待加入的元素比较,返回小于0的数则放在二叉树的左边,大于0则放在二叉树右边,等于零则覆盖
2.Comparator接口,该接口需要实现compare()方法,它的两个参数分别是待插入节点和集合中的元素
3.TreeSet/TreeMap也可以****通过构造器传递该比较器实现类对象实现比较规则,通常用于比较规则进场发生改变的类,原理和compareTo()方法类似,在TreeSet集合底层,如果传递了Comparator比较器类对象,则优先使用Comparator比较器的比较规则
五.Collections工具类
常用方法:
static void copy(List<? super T> dest, List<? extends T> src) | 将所有的元素从一个列表复制到另一个列表中。 |
static <T extends Comparable<? super T>>void sort(List list) | 指定列表为升序排序(集合中的元素排序需要实现Comparable接口) |
static void sort(List list, Comparator<? super T> c) | 根据指定的比较器指定的顺序对指定的列表进行排序。 |
static List synchronizedList(List list) | 返回由指定列表支持的同步(线程安全)列表。 |
创作不易,对您有所帮助的话可以点个赞支持下哦!!!