java进阶(五)_集合
Java集合
1. JAVA集合框架概述
1.1概述
-
集合、数组都是对多个数据进行存储操作的结构,简称Java容器
说明:此时的存储,主要指内存层面的存储,不涉及持久化的存储
-
数据在存储多个数据方面的特点
- 一旦初始化后,其长度就确定了;
- 数组一旦定义好,其元素的类型也就确定了。我们只能操作指定类型的数据;
- 数组在存储多个数据方面的缺点:
- 一旦初始化后,长度不可修改;
- 数组中提供的方法非常有限,对于添加、删除、插入数据等操作,非常不便,并且效率不高;
- 获取数组中实际元素的个数的需求,数组没有现成的属性或方法可用;
- 数组存储数据的特点:有序,可重复。对于无序、不可重复的需求,不能满足。
1.2 集合框架
Java集合可分为Collection和Map两种体系:
- Collection接口:单列数据,定义了存取一组对象的方法的集合
- List:元素有序、可重复的集合,也就是存储有序的、可重复的数据。--->”动态“数组,替换原有的数组
- ArrayList、LinkedList、Vector
- Set:元素无序、不可重复的集合。-->高中讲的”集合“
- HashSet、LinkedHashSet、TreeSet
- List:元素有序、可重复的集合,也就是存储有序的、可重复的数据。--->”动态“数组,替换原有的数组
- Map接口:双列数据,保存具有映射关系”key-value“对的集合。-->高中讲的”函数“:y=f(x)
- HashMap、LinkedHashMap、TreeMap、Hashtable、Properties
2. Collection接口中常用的方法
-
添加
- add(Object obj),注意添加的是对象!!添加的整型数据会自动装箱。
- addAll(Collection coll)
-
获取有效元素的个数
- int size()
-
清空集合
- void clear()
-
是否是空集合
- boolean isEmpty()
-
是否包含某个元素
- boolean contains(Object obj):是通过equals方法 (也就是只是判断内容是否相同)判断是否包含同一个对象。
- 要求:向Collection接口的实现类的对象中添加数据obj时,要求obj所在类要重写equals().
- boolean containsAll(Collection c):也是调用元素的equals方法来比较的,拿两个集合的元素依次比较。
- boolean contains(Object obj):是通过equals方法 (也就是只是判断内容是否相同)判断是否包含同一个对象。
-
删除
- boolean remove(Object obj):是通过元素的equals方法判断是否是要删除的那个元素。只会删除找到的第一个元素
- removeAll(Collection coll):取当前集合的差集。从当前集合中移除coll中的所有的元素。
-
取两个集合的交集
- retainAll(Collection coll):把交集的结果存在当前集合中,不影响coll
-
集合是否相等
- boolean equals(Object obj):要想返回True,需要当前集合和形参集合中的元素都相同。
-
转成对象数组
- Object[] toArray()
- 数组转成集合:调用Arrays类的静态方法asList()
-
获取集合对象的哈希值
- hashCode():返回当前对象的hash值。
-
遍历
- iterator():返回迭代器对象,用于集合遍历。即返回Iterator接口的实例,用于遍历集合元素。
3. Iterator迭代器接口
-
Iterator对象称为迭代器(设计模型的一种),主要用于遍历Collection集合中的元素。
-
Iterator仅用于遍历集合,Iterator本身并不提供承载对象的能力。如果需要创建Iterator对象,则必须有一个被迭代的集合
-
集合对象每次调用iterator()方法都得到一个全新的迭代器对象,默认游标都在集合的第一个元素之前。
-
内部方法:hasNext() 和next()
@Test
public void test1(){
Collection coll=new ArrayList();
coll.add(123);//自动装箱
coll.add(456);
coll.add("吃饭");
Iterator iterator=coll.iterator();
//hasNext()判断是否还有下一个元素
while(iterator.hasNext()){
//next():①指针下移;将②下移后集合位置上的元素返回
System.out.println(iterator.next());
//iterator.remove();
}
}
- 内部还定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()。
- 如果还未调用next()或者上次调用next方法之后已经调用了remove方法,再调用remove都会报IllegalStateException
4.Collection子接口之一:List接口
4.0 List接口的各个实现类特点
List:元素有序、可重复的集合,也就是存储有序的、可重复的数据。--->”动态“数组,替换原有的数组
ArrayList:作为List接口的主要实现类;线程不安全的,效率高;底层使用Object[] elementDate存储,使用的是顺序表存储;
LinkedList:对于频繁的插入、删除操作,使用此类比ArrayList的效率高;底层使用双向链表存储
Vector:作为List接口的古老实现类;线程安全的,效率低;底层使用Object[] elementDate存储
面试题:ArrayList、LinkedList、Vector的异同?
4.1 ArrayList源码分析
(1)jdk 7情况下
ArrayList list=new Arraylist();//底层创建了长度是10的Object[]数组elementData
list.add(123);//elementDate[0]=new Integer(123);
list.add(11);//如果此次的添加导致底层的elementData数组容量不够,则扩容
默认情况下,扩容为原来容量的1.5倍,同时需要将原有数组中的数据复制到新的数组中。
* 结论:建议开发中使用带参的构造器:ArrayList list=new ArrayList(int capacity)
(2)jdk 8情况下,ArrayList的变化:
ArrayList list=new Arraylist();//底层 Object[] elementDate 初始化为{},并没有创建长度
list.add(123);//第一次调用add()时,底层才创建了长度为10的数组,并将数据123添加到elementData数组中。
* 后续添加与jdk7.0无异。
- (3)小结:jdk 7中ArrayList 的对象的创建类似于单例的饿汉式,而jdk8.0中的ArrayList的对象的创建类似于单列的
* 懒汉式,延迟了数组的创建,节省内存。
4.2 LinkedList源码分析
LinkedList list=new LinkedList();//内部声明了Node类型的first和last属性,默认值为null
list.add(123);//将123封装到Node中,创建了Node对象
//其中,Node的定义体现了LinkedList的双向链表的说法
4.3 Vector的源码分析
jdk7和jdk8中通过Vector()构造器创建对象时,底层都创建了长度为10的数组;在扩容方面,默认扩容为原来的数组长度的2倍。
4.4 List接口方法
-
List除了从Collection集合继承的方法外,List集合里添加了一些根据索引来操作集合元素的方法。
- void add(int index, Object ele):在index位置插入ele元素
- Object get(int index):获取指定index位置的元素
- Object remove(int index):移除指定index位置的元素,并返回此元素
- Object set(int index, Object ele):设置指定index位置的元素为ele
-
总结:常用方法
增:add(Object obj)
删:remove(int index)/remove(Object obj):区别就是删索引对应的元素还是删除该对象
改:set(int index, Object ele)
查:get(int index)
长度:size()
遍历:①迭代器方式;②增强for循环;③普通循环
5.Collection子接口之一:Set接口
5.0 Set接口的实现类的特点
- set接口没有提供额外的方法,使用的都是Collection中定义过的方法
- Set集合不允许包含相同的元素
- Set判断两个对象是否相同不是使用==运算符,而是根据equals()方法
Set:存储无序的、不可重复的数据。-->高中讲的”集合“
HashSet:作为Set接口的主要实现类;线程不完全的;可以存储null值
LinkedHashSet:是HashSet的一个子类;遍历其内部数据时,可以按照添加的顺序遍历
TreeSet:可以按照添加对象的指定属性,进行*排序*
5.1理解(以HashSet为例)
(1)Set的特点 (以HashSet为例)
- 无序性:不等于随机性。存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据元素的哈希值决定。
- 不可重复性:保证添加的元素按照equals()判断时,不能返回true,即,相同的元素只能添加一个。
(2)添加元素的过程:以HashSet为例
- 向HashSet中添加元素a,首先调用元素a所在类的hasCode()方法,计算元素a的哈希值,此哈希值接着通过某种算法算出在HashSet底层数组中的存放位置(即:索引位置),判断数组此位置上是否已经有元素;
- 如果此位置上没有其他元素,则元素a添加成功;---情况1
- 如果此位置上有其他元素b(或者以链表形式存在的多个元素),则比较元素a和元素b的hash值:
- 如果hash值不相同,则元素a添加成功。--情况2
- 如果hash值相同,进而需要调用元素a所在类的equals()方法:
- equals()返回ture,元素a添加失败
- equals()返回false,则元素a添加成功。---情况3
对于添加成功的情况2和情况3而言:元素a与已经存在指定索引位置上数据以链表的方式存储。
jdk7:元素放到数组中,指向原来的元素
jdk8:原来的元素放到数组中,指向元素a
总结:七上八下
HashSet底层:数组+链表方式存储
(3)要求:向Set中添加的数据,其所在的类一定要重写hashCode()和equals();并且重写的两个方法要尽可能保持一致性。
5.2 LinkedHashSet的使用
LinkedHashSet作为HashSet的子类,在添加数据的同时,每一个数据还维护了两个引用,记录此数据的前一个数据和后一个数据。优点:对于频繁的遍历操作,LinkedHashSet效率高于HashSet。
5.3 TreeSet
-
向TreeSet中添加的数据,要求是相同类的对象
-
涉及到两种排序方式:
- 自然排序(实现Comparable接口)
- 定制排序(Comparator)
-
自然排序中,比较两个对象是否相同的标准为:compareTo()返回0,不再是equals();
-
定制排序中,比较两个对象是否相同的标准为:compare()返回0,不再是equals()。
6. Map接口
6.0 Map接口的各个实现类特点和理解
(1)Map:双列数据,保存具有映射关系”key-value“对的集合。-->高中讲的”函数“:y=f(x)
-
HashMap:作为Map的主要实现类;线程不安全的,效率高;存储null的key和value
- LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。原因:在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素,对于频繁的遍历操作,此类执行效率高于HashMap。
-
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。此时考虑key的自然排序或者定制排序,底层使用红黑树。
-
Hashtable:作为古老的实现类;线程安全的,效率低;不能存储null的key和value
- Properties:是Hashtable的子类,常用来处理配置文件。key和value都是String类型。存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法
HashMap的底层:数组+链表(jdk7及之前),数组+链表+红黑树(jdk8)
面试题
- HashMap的底层实现原理?
- HashMap和Hashtable的异同?
- CurrentHashMap与Hashtable的异同?
(2)Map结构的理解
Map中的key:无序、不可重复的,使用Set存储所有的key;
Map中的value:无序的,可以重复,使用Collection存储所有的value;
一个键值对:key-value构成了一个Entry对象;
Map中的entry:无序的、不可重复,使用Set存储所有的entry
6.1 HashMap的底层实现原理?以jdk7为例说明:
-
HashMap map=new HashMap():
在实例化以后,底层创建了长度是16的一维数组Entry[ ] table,
……可能已经执行了多次put……
-
map.put(key1,value1):
首先,调用key1所在类的hashCode( )计算key1哈希值,此哈希值经过某种算法计算以后,得到在Entry数组中的存放位置,
- 如果此位置上的数据为空,此时key1-value1添加成功;---情况1
- 如果此位置上的数组不为空,(意味着此位置上存在一个或多个数据(以链表形式存在)),比较key1和已经存在的一个或多个数据的哈希值:
- 如果key1的哈希值与已经存在的数据的哈希值都不相同,此时key1-value1添加成功;---情况2
- 如果key1的哈希值与已经存在某个数据(key2-value2)的哈希值相同,继续比较;调用key1所在类的equals(key2),
- 如果equals( )返回false:此时key1-value1添加成功;---情况3
- 如果equals( )返回true:使用value1替换value2。
补充:关于情况2和情况3:此时key1-value1和原来的数据以链表的方式存储。
在不断的添加过程中,会涉及到扩容问题,默认的扩容方式:扩容为原来容量的2倍,并将原来的数据复制过来。
-
jdk8相较于jdk7在底层实现方面的不同:
-
new HashMap( ):底层没有创建一个长度为16的数组
-
jdk8底层的数组是:Node[],而非Entry[]
-
首次调用put( )方法时,底层创建长度为16的数组
-
Jdk7底层结构只有:数组+链表。jdk8中的底层结构:数组+链表+红黑树。
当数组的某一个索引位置上的元素以链表形式存在的数据个数>8且当前数组的长度>64时,此时索引位置上的所有数据改为使用红黑树存储。
-
DEFAULT_INITIAL_CAPACITY:HashMap的默认容量,16
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子:0.75
threshold:扩容的临界值=容量*加载因子:16*0.75=12
TREEIFY_THRESHOLD:Bucket中链表长度大于默认值,转化为红黑树:8
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量:64
6.2 LinkedHashMap的底层实现原理
源码中:
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);
}
}
6.3 Map接口:常用方法
//1.添加、删除、修改操作
Object put(Object key,Object value);//将指定key-value添加到(或修改)当前map对象中
void putAll(Map m);//将m中所有的key-value对存放到当前map中
Object remove(Object key);//移除指定key的key-value对,并返回value
void clear();//清空当前map中的所有数据
//2.元素查询操作
Object get(Object key);//获取指定key对应的value
boolean containsKey(Object key);//是否包含指定的key
boolean containsValue(Object value);//是否包含指定的value
int size();//返回map中key-value对的个数
boolean isEmpty();//判断当前map是否为空
boolean equals(Object obj);//判断当前map和参数对象obj是否相等
//3.元视图操作的方法
Set keySet();//返回所有key构成的Set集合
Collection values();//返回所有value构成的Collection集合
Set entrySet();//返回所有key-value对构成的Set集合
总结常用方法
- 添加: put(Object key,Object value);
- 删除:remove(Object key);
- 修改: put(Object key,Object value);
- 查询:get(Object key);//获取指定key对应的value
- 长度:size();
- 遍历:keySet( )/values( )/entrySet( )
6.4 TreeMap两种添加方式的使用
向TreeMap中添加key-value,要求key必须是由同一个类创建的对象
因为要按照key进行排序:自然排序、定制排序
7. Collections工具类
-
Collections是一个操作Set、List和Map等集合的工具类,之前将操作数组的工具类:Arrays;
-
Collections中提供了一系列静态的方法对集合元素进行排序、查询和修改等操作,还提供了对集合对象设置不可变、对集合对象实现同步控制等方法。
-
排序操作:(均为static方法)
- reverse(List):反转List元素中的顺序
- shuffle(List):对List集合元素进行随机排序
- sort(List):根据元素的自然顺序对指定List集合元素按升序进行排序
- sort(List,Comparator):根据指定的Comparator产生的顺序对List集合元素进行排序
- swap(List,int i ,int j):将指定list集合中的i处元素和j处元素进行交换。
-
查找、
- Object max(Collections):
- Object max(Collections,Comparator):
- Object min(Collections):
- Object min(Collections,Comparator):
- int frequency(Collections,Object );
- void copy(List dest,List src);将src的内容复制到dest
- boolean replaceAll(List list,Object newVal);
-
Collections常用方法:同步控制
- Collections类中提供了多个syncharonizedXxx( )方法,该方法可使将指定集合包装成线程同步的集合,从而可以解决多线程并发访问集合时的线程安全问题。
面试题:Collections和Collection的区别?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)