Java基础学习——第十一章 Java集合
一、Java集合框架概述
1. Java容器:数组与集合
面向对象编程对事物的体现都是以对象的形式,为了便于对多个对象进行操作,就要对对象进行存储 Java容器:集合和数组都是对多个数据进行存储(内存层面)的结构;① 数组在存储对象方面存在着一些弊端;② 集合可以动态地 把多个对象的引用 放入容器中 数组在存储多个数据方面的弊端:
数组一旦初始化以后,长度就不可变了,不便于扩展 数组一旦声明类型后,其元素的类型也就确定了,不能再存储其他类型的数据(多态情况除外) 数组中提供的属性和方法少,不便于进行添加、删除、插入等操作, 且效率不高。同时无法直接获取存储元素的个数 数组存储的数据是有序的、可以重复的 —> 存储数据的特点单一 Java集合类可以用于存储数量不等的多个对象 ,还可用于保存具有映射关系的关联数组
2. 集合的使用场景
3. 集合的两种体系:Collection & Map
Java 集合可分为 Collection 和 Map 两种体系
Collection接口 : 单列集合 ,用来存储一个一个 的==对象 ==
List子接口 : 元素有序 、可重复 的集合 —> 动态“数组”
实现类:ArrayList、LinkedList、Vector Set子接口 : 元素无序 、不可重复 的集合 —> 数学中的“集合”
实现类:HashSet、LinkedHashSet、TreeSet Map接口 : 双列集合 ,用来存储一对一对 的,具有==映射关系“key-value” ==的数据 —> 函数:y = f(x)
实现类:HashMap、LinkedHashMap、TreeMap、Hashtable、Properties 注意:元素有序/无序是指在当前集合中,存储的对象在底层数组中是否按照数组的索引顺序添加
3.1 Collection接口继承树
3.2 Map接口继承树
二、Collection接口
1. Collection接口概述
Collection接口是 List、Set和Queue接口的父接口 ,该接口中定义的方法可用于操作List、Set和Queue集合 JDK不提供 Collection接口的任何直接实现类 ,而是提供 更具体的子接口 ,如:List 和 Set Collection接口:单列集合,用来存储一个一个的对象;对于基本数据类型元素,实际存储的是其包装类对象 在JDK5.0之前, Java集合会丢失容器中所有对象的数据类型,把所有对象都当成Object类型处理;从JDK5.0增加了泛型以后,Java 集合可以记住容器中对象的数据类型
2. Collection接口中的常用方法
通常向Collection接口的实现类对象(集合)中添加对象时,要求该对象所在类重写equals()方法,比较对象的实际内容
添加(增):将指定对象(或指定集合中的所有元素)添加到当前集合中(末尾位置)
add(Object obj):将对象obj添加到当前集合中 addAll(Collection coll):将另一个集合coll中的元素全部添加到当前集合中 获取当前集合中有效元素的个数
清空当前集合中的所有元素
判断当前集合是否为空集合,即size()方法的返回值是否等于0
判断当前集合是否包含某个对象obj(或另一个集合c中的所有元素)
boolean contains(Object obj):调用形参对象obj所在类的equals()方法来判断 boolean containsAll(Collection c):调用形参集合c中的元素所在类的equals()方法来判断,挨个比较 删除(删):从当前集合中删除指定元素(或和指定集合的交集元素)
boolean remove(Object obj):调用形参对象obj所在类的equals()方法来判断当前集合中的元素是否为要删除的元素,仅删除第一个找到的元素 boolean removeAll(Collection coll):取两个集合的差集,调用形参集合c中的元素所在类的equals()方法来判断,挨个比较 取两个集合的交集
boolean retainAll(Collection c):将返回的交集元素存储在当前调用该方法的集合中,不影响形参集合c 判断两个集合是否相等:比较两个集合中的元素内容是否一致(如果是List的实现类还需比较元素排列的顺序是否完全一致)
boolean equals(Object obj):形参对象obj应当为Collection接口的实现类的对象(集合) 将当前集合转化成对象数组:集合 --> 数组
Object[] toArray() 拓展:数组 --> 集合:调用Arrays类的静态方法asList() 返回当前集合对象的哈希值
遍历:返回一个实现了Iterator接口的迭代器对象,用于遍历 Collection 集合元素
import org. junit. Test ;
import java. util. * ;
public class CollectionTest {
@Test
public void test1 ( ) {
Collection coll = new ArrayList ( ) ;
coll. add ( "AA" ) ;
coll. add ( "BB" ) ;
coll. add ( 123 ) ;
coll. add ( new Date ( ) ) ;
System . out. println ( coll. size ( ) ) ;
Collection coll1 = new ArrayList ( ) ;
coll1. add ( 456 ) ;
coll1. add ( "CC" ) ;
coll1. addAll ( coll) ;
System . out. println ( coll1. size ( ) ) ;
System . out. println ( coll1) ;
coll1. clear ( ) ;
System . out. println ( coll1. isEmpty ( ) ) ;
System . out. println ( coll. contains ( 123 ) ) ;
System . out. println ( coll. contains ( new String ( "AA" ) ) ) ;
Collection coll2 = Arrays . asList ( 123 , "AA" ) ;
System . out. println ( coll. containsAll ( coll2) ) ;
coll. remove ( "AA" ) ;
System . out. println ( coll) ;
coll. remove ( new String ( "BB" ) ) ;
System . out. println ( coll) ;
Collection coll3 = Arrays . asList ( 123 , 4567 ) ;
coll. removeAll ( coll3) ;
System . out. println ( coll) ;
Collection coll4 = new ArrayList ( ) ;
coll4. add ( 123 ) ;
coll4. add ( 456 ) ;
coll4. add ( "AA" ) ;
coll4. add ( new String ( "BB" ) ) ;
Collection coll5 = Arrays . asList ( 123 , 456 , "CC" ) ;
coll4. retainAll ( coll5) ;
System . out. println ( coll4) ;
System . out. println ( coll5) ;
Collection coll6 = Arrays . asList ( 123 , 456 ) ;
System . out. println ( coll4. equals ( coll6) ) ;
Collection coll7 = Arrays . asList ( 456 , 123 ) ;
System . out. println ( coll4. equals ( coll7) ) ;
Object [ ] array = coll7. toArray ( ) ;
for ( int i = 0 ; i < array. length; i++ ) {
System . out. print ( array[ i] + " " ) ;
}
System . out. println ( ) ;
List < String > list = Arrays . asList ( new String [ ] { "AA" , "BB" , "CC" } ) ;
System . out. println ( list) ;
System . out. println ( coll7. hashCode ( ) ) ;
}
}
三、Iterator迭代器接口
1. Iterator接口概述
Iterator 对象称为迭代器(设计模式的一种),专门用于遍历 Collection 接口的实现类对象(集合)中各个元素 迭代器模式的定义为:提供一种方法访问一个容器对象中各个元素,而又无需暴露该对象的内部细节。 迭代器模式,就是为容器而生 Collection接口继承了java.lang.Iterable接口,该接口有一个iterator()方法,因此所有实现了Collection接口的集合类都有一个iterator()方法,用以 返回一个实现了Iterator接口的迭代器对象 Iterator仅用于遍历集合 ,Iterator本身并不提供承装对象的能力 。如果需要创建Iterator对象,则必须有一个被迭代的集合
2. Iterator接口常用方法
boolean hasNext() :判断当前指针所处位置是否还有下一个元素next() :①指针下移一位;②返回下移后的位置对应的集合元素void remove() :删除通过当前迭代器的next()方法返回的最后一个集合元素
3. 使用Iterator迭代器对象遍历Collection集合元素
对于某个实现了Collection接口的集合类对象,调用其iterator()方法,返回一个实现了Iterator接口的迭代器对象,默认指针在集合的第一个元素之前
注意:创建迭代器不能使用匿名对象,集合对象每次调用iterator()方法 都会返回一个全新的迭代器对象 ,指针都会复位到集合的第一个元素之前 在调用迭代器对象的next()方法之前必须先调用hasNext()方法进行检测,判断当前指针所处位置是否还有下一个元素 。若不调用,且下一条记录无效,直接调用next()方法会抛NoSuchElementException异常 调用迭代器对象的next()方法:①指针下移一位;②返回下移后的索引对应的集合元素
import org. junit. Test ;
import java. util. ArrayList ;
import java. util. Collection ;
import java. util. Iterator ;
public class IteratorTest {
@Test
public void test1 ( ) {
Collection coll = new ArrayList ( ) ;
coll. add ( 123 ) ;
coll. add ( 456 ) ;
coll. add ( "AA" ) ;
coll. add ( new String ( "Tom" ) ) ;
coll. add ( false ) ;
Iterator iterator = coll. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
}
}
4. Iterator接口中的remove()方法
void remove():删除通过当前迭代器的next()方法返回的最后一个集合元素 Iterator可以删除集合的元素,但是是在遍历过程中通过调用迭代器对象的remove方法,不是集合对象的remove方法 如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException
import org. junit. Test ;
import java. util. ArrayList ;
import java. util. Collection ;
import java. util. Iterator ;
public class IteratorTest {
@Test
public void test2 ( ) {
Collection coll = new ArrayList ( ) ;
coll. add ( 123 ) ;
coll. add ( 456 ) ;
coll. add ( "AA" ) ;
coll. add ( new String ( "Tom" ) ) ;
coll. add ( false ) ;
Iterator iterator = coll. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Object obj = iterator. next ( ) ;
if ( "Tom" . equals ( obj) ) {
iterator. remove ( ) ;
}
}
Iterator iterator1 = coll. iterator ( ) ;
while ( iterator1. hasNext ( ) ) {
System . out. println ( iterator1. next ( ) ) ;
}
}
}
5. 使用foreach循环(增强for)遍历Collection集合元素
JDK5.0新增了 foreach 循环(增强 for 循环),用于迭代遍历 Collection 集合和数组 foreach 遍历无需获取 Collection 集合或数组的长度,无需使用索引访问指定元素 foreach 遍历集合的底层调用 Iterator 迭代器接口完成 foreach 还可以用来遍历数组 foreach 循环遍历集合or数组的格式:for (集合或数组元素的类型 局部变量 : 集合或数组对象的名称) {}
import org. junit. Test ;
import java. util. ArrayList ;
import java. util. Collection ;
public class ForeachTest {
@Test
public void test1 ( ) {
Collection coll = new ArrayList ( ) ;
coll. add ( 123 ) ;
coll. add ( 456 ) ;
coll. add ( "AA" ) ;
coll. add ( new String ( "Tom" ) ) ;
coll. add ( false ) ;
for ( Object obj : coll) {
System . out. println ( obj) ;
}
}
@Test
public void test2 ( ) {
int [ ] arr = new int [ ] { 1 , 2 , 3 , 4 , 5 , 6 } ;
for ( int i : arr) {
System . out. println ( i) ;
}
}
}
5.1 增强for循环面试题
@Test
public void test3 ( ) {
String [ ] arr = new String [ ] { "MM" , "MM" , "MM" } ;
for ( String s : arr) {
s = "GG" ;
System . out. println ( s) ;
}
for ( int i = 0 ; i < arr. length; i++ ) {
System . out. println ( arr[ i] ) ;
}
}
四、Collection子接口一:List接口
1. List接口概述
鉴于Java中数组用来存储数据的局限性,我们通常使用List替代数组 --> 动态“数组” List集合类中 元素有序、可重复 ,集合中的每个元素都有其对应的顺序索引 List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根据序号存取容器中的元素 常用的List接口的实现类:① ArrayList;② LinkedList;③ Vector
2. List接口实现类之一:ArrayList
ArrayList 是 List 接口的主要实现类 线程不安全的,执行效率高 底层使用Object类型的数组 存储(顺序存储) 本质上,ArrayList实现了基于“动态数组“的数据结构 对于随机访问get和set,ArrayList的效率比LinkedList高 ,因为LinkedList要移动指针 ArrayList每次扩容 为原来容量的1.5倍
2.1 ArrayList源码分析:JDK7.0
JDK7.0的ArrayList实现像单例模式的饿汉式:在实例化ArrayList对象时,底层直接创建一个初始长度为10的Object类型的对象数组elementDate 若某项操作导致底层数组elementDate容量不足,则进行扩容(默认为1.5倍):本质上新建一个长度为原来长度1.5倍的数组,并将原有数组中的元素按次序复制到新数组中 Arrays.copyOf() 在实际开发中,当需要实例化ArrayList对象时,推荐使用带参构造器指定ArrayList集合的初始容量
ArrayList list = new ArrayList ( ) ;
list. add ( 123 ) ;
. . .
list. add ( 11 ) ;
ArrayList list1 = new ArrayList ( int initialCapacity) ;
2.2 ArrayList源码分析:JDK8.0的变化
JDK8.0的ArrayList实现像单例模式的懒汉式:在实例化ArrayList对象时,底层Object类型的对象数组elementDate初始化为{},即数组长度为0;当添加第一个元素时,再创建一个初始长度为10的Object[] JDK8.0的改进延迟了ArrayList对象在实例化时底层数组的创建,节省了内存
ArrayList list = new ArrayList ( ) ;
list. add ( 123 ) ;
. . .
3. List接口实现类之二:LinkedList
底层使用==双向链表 ==存储 对于频繁的==插入和删除操作,LinkedList的效率比ArrayList高 ==
3.1 LinkedList源码分析
LinkedList中声明了内部类Node ,作为LinkedList存储数据的基本结构 ,即LinkedList对象中的==每个元素实际上都是一个Node类的对象 =。每个Node对象声明了三个属性:
**item:**用于存储当前集合元素的真实数据,指向实际存储的对象 **prev:**指向前一个元素(Node类的对象) **next:**指向下一个元素(Node类的对象)
在实例化LinkedList对象时:底层并没有声明数组,而是声明了 Node 类型的 first 和 last 属性(引用类型变量,用于指向双向链表首末位置的元素(Node类的对象)),默认值为null 当调用**add()**方法往LinkedList对象中添加元素时:
① 创建了一个新的Node对象,该Node对象的item属性用于存储该元素的真实数据,指向实际要存储的对象;prev属性指向前一个元素(Node类的对象);next属性默认初始化为null ② 修改前一个元素(Node类的对象)的next属性,使其指向当前新建的Node对象 ③ 修改LinkedList对象的last属性,指向新建的Node对象;若该新建的Node对象为该集合的首个元素,则使first属性也指向该新建的Node对象
LinkedList list = new LinkedList ( ) ;
list. add ( 123 ) ;
3.2 LinkedList新增方法
void addFirst(Object obj) void addLast(Object obj) Object getFirst() Object getLast() Object removeFirst() Object removeLast()
4. List接口实现类之三:Vector(基本不用)
List接口的古老实现类,大多数操作与ArrayList相同 线程安全的(Vector类中的方法大多为同步方法),执行效率低 底层使用Object类型的数组 存储(顺序存储) Vector每次扩容 为原来容量的2倍
面试题:比较ArrayList & LinkedList & Vector
三者的共同点:
① 都是List接口的实现类 ② 存储数据的特点相同:单列集合,用来存储一个一个的对象,元素有序、可重复 三者的不同点:
ArrayList(JDK1.0):
① List接口的主要实现类 ② 线程不安全的,执行效率高 ③ 底层使用Object类型的数组 存储(顺序存储) ④ 对于随机访问get和set,ArrayList的效率比LinkedList高 ,因为LinkedList要移动指针 ⑤ ArrayList每次扩容 为原来容量的1.5倍 LinkedList(JDK1.2):
① 底层使用双向链表 存储 ② 对于频繁的插入和删除操作,LinkedList的效率比ArrayList高 Vector(JDK1.0):
① List接口的古老实现类,大多数操作与ArrayList相同,基本不用了 ② 线程安全的(Vector类中的方法大多为同步方法),执行效率低 ③ 底层使用Object类型的数组 存储(顺序存储) ④ Vector每次扩容 为原来容量的2倍
5. List接口常用方法
List接口中除了包含从Collection接口继承的方法外,还添加了一些根据索引来操作集合元素的方法
void add(int index, Object ele) :在index位置插入ele元素boolean addAll(int index, Collection eles) :从index位置开始将eles中的所有元素插入进来Object get(int index) :获取指定index位置的元素int indexOf(Object obj) :返回对象obj在集合中首次出现的位置int lastIndexOf(Object obj) :返回对象obj在当前集合中最后一次出现的位置Object remove(int index) :删除指定index位置的元素,并返回此元素Object set(int index, Object ele) :设置指定index位置的元素为eleList subList(int fromIndex, int toIndex) :返回从索引fromIndex到toIndex的子集合(左闭右开)
import org. junit. Test ;
import java. util. ArrayList ;
import java. util. Arrays ;
import java. util. Date ;
import java. util. List ;
public class ListTest {
@Test
public void test1 ( ) {
ArrayList list = new ArrayList ( ) ;
list. add ( 123 ) ;
list. add ( 456 ) ;
list. add ( "AA" ) ;
list. add ( new Date ( ) ) ;
list. add ( 456 ) ;
System . out. println ( list) ;
list. add ( 1 , "BB" ) ;
System . out. println ( list) ;
List list1 = Arrays . asList ( 1 , 2 , 3 ) ;
list. addAll ( 1 , list1) ;
System . out. println ( list) ;
System . out. println ( list. get ( 1 ) ) ;
System . out. println ( list. indexOf ( 456 ) ) ;
System . out. println ( list. lastIndexOf ( 456 ) ) ;
Object remove = list. remove ( 1 ) ;
System . out. println ( list) ;
System . out. println ( remove) ;
list. remove ( new Integer ( 456 ) ) ;
System . out. println ( list) ;
list. set ( 1 , "CC" ) ;
System . out. println ( list) ;
List list2 = list. subList ( 0 , 3 ) ;
System . out. println ( list2) ;
}
}
5.1 遍历List集合元素的方法(以ArrayList为例)
方式一:使用Iterator迭代器对象遍历集合元素 方式二:使用foreach循环(增强for)遍历集合元素 方式三:使用普通for循环,通过调用get(int index)方法
public class ListTest {
@Test
public void test2 ( ) {
ArrayList list = new ArrayList ( ) ;
list. add ( 123 ) ;
list. add ( 456 ) ;
list. add ( "AA" ) ;
Iterator iterator = list. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
for ( Object obj : list) {
System . out. println ( obj) ;
}
for ( int i = 0 ; i < list. size ( ) ; i++ ) {
System . out. println ( list. get ( i) ) ;
}
}
}
5.2 总结:List接口常用方法(必须掌握)
增:① add(Object obj);② addAll(Collection coll) 删:① remove(Object obj);② remove(int index) 改:① set(int index, Object ele) 查:① get(int index) 插:① add(int index, Object ele);② addAll(int index, Collection eles) 集合中有效元素个数:① size() 遍历:① 使用Iterator迭代器对象遍历集合元素;② 使用foreach循环(增强for)遍历集合元素; ③ 使用普通for循环,通过调用get(int index)方法
五、Collection子接口二:Set接口
1. Set接口概述
Set接口是Collection的子接口, 但set接口没有提供额外的方法 ,使用的都是Collection接口中声明的方法 Set集合类中 元素无序、不可重复 对于存放在Set集合中的对象,对应的类一定要重写equals()和hashCode()方法,以保证当两个对象的equals()方法比较返回 true 时,这两个对象的hashCode()方法的返回值也应相等 常用的Set接口的实现类:① HashSet;② LinkedHashSet;③ TreeSet
2. Set接口实现类之一:HashSet
HashSet 是 Set 接口的主要实现类,大多数时候使用 Set 集合时都使用这个实现类 HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能 HashSet底层:数组+链表的结构 ;底层数组初始容量为16,当使用率超过0.75时,就会扩容为原来的2倍线程不安全 集合元素可以是==null ==
2.1 理解元素无序 & 不可重复(以HashSet为例)
无序性 :不等同于随机性。对于HashSet集合而言,其存储的对象在底层数组中并非按照数组的索引顺序添加,而是通过调用对象所在类的hashCode()方法的返回值(hash值) ,再通过散列函数 计算存储的位置不可重复性 :HashSet判断两个对象相等的标准:
① 当前要添加对象的hashCode()方法返回值 与集合中元素的hashCode()方法返回值相同 ② 本质上:调用当前要添加对象所在类的equals()方法 与集合中元素进行判断时,返回true
2.2 向HashSet中添加元素的过程
当向HashSet集合中添加(add)一个对象时,首先调用该对象所在类的hashCode()方法 ,返回==该对象的hash值 == 根据该hash值,通过某种==散列函数 ==计算当前对象在HashSet底层数组中应当存储的位置**(索引)**
散列函数会根据当前对象的哈希值和底层数组的长度计算得到该对象在数组中的索引 散列函数应当尽可能保证能均匀存储数据,越是散列分布,该散列函数设计的越好 判断在HashSet底层数组中,该索引位置上是否已存在元素:
若该位置上没有其他元素,则当前对象添加成功 —> 添加成功:情况1 若该位置上已有其他元素(一个对象或以链表形式存储的多个对象),则比较当前对象与该索引位置上所有元素的hash值(hashCode方法的返回值)
若当前对象与该索引位置上所有元素的hash值都不相同,则当前对象添加成功 —> 添加成功:情况2 若当前对象与该索引位置上某元素的hash值相同,则再**调用当前对象所在类的equals()方法 **进行比较
若equals()方法返回false,则当前对象添加成功 —> 添加成功:情况3 若equals()方法返回true,则表明当前对象与数组中元素相同,添加失败
总结:hashCode()方法的返回值(hash值)决定了当前对象在HashSet底层数组中应当存储的位置;当该索引位置已有其他元素时,只要hashCode()返回值不同,就一定代表是不同的元素 ;若hashCode值相同,则最后只能通过当前对象的equals()方法 来判断 对于添加成功的情况2和情况3:当前对象与该索引位置上原有的元素以==链表 ==的方式进行存储:
JDK7.0:当前对象(最新添加的对象)作为链表的head,存放到数组中,指向原有的元素 JDK8.0:原有的元素存放在数组中,指向当前对象(最新添加的对象) ——作为链表的last
2.3 重写hashCode()和equals()方法的原则
重写 hashCode() 方法的基本原则:
同一个对象多次调用 hashCode() 方法应该返回相同的值 ① 当两个对象调用equals()方法进行比较返回true时,这两个对象的hashCode()方法的返回值也应相等(相等的对象必须具有相等的散列码 ) ② 当两个对象的hashCode()方法的返回值不相等时,这两个对象调用equals()方法进行比较返回false 在重写equals()方法时比较的属性,都应当用来计算hashCode值 重写 equals() 方法的基本原则:
重写equals()方法的时候一般都需要同时重写hashCode()方法。通常参与计算hashCode的属性也应该参与到equals()方法中进行比较 为什么用IDEA重写hashCode方法,有31这个数字:
主要作用是放大不同对象hashCode的差异,减少冲突,所以乘了这个系数 选择系数的时候要选择尽量大的系数。因为如果计算出来的hash地址越大,冲突就越少,查找效率也越高 31只占用5bits,相乘造成数据溢出的概率较小 i * 31 == (i << 5) - 1 31是一个质数
3. Set接口实现类之二:LinkedHashSet
LinkedHashSet 是 HashSet 的子类 LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,但它同时使用双向链表 维护元素的次序,可以按照添加的顺序遍历集合元素 在需要频繁遍历Set元素时,LinkedHashSet的效率比HashSet高
public class SetTest {
@Test
public void test1 ( ) {
Set set = new HashSet ( ) ;
set. add ( 456 ) ;
set. add ( 123 ) ;
set. add ( "AA" ) ;
set. add ( new Date ( ) ) ;
set. add ( 129 ) ;
set. add ( 129 ) ;
Iterator iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. print ( iterator. next ( ) + " " ) ;
}
System . out. println ( ) ;
Set set1 = new LinkedHashSet ( ) ;
set1. add ( 456 ) ;
set1. add ( 123 ) ;
set1. add ( "AA" ) ;
set1. add ( new Date ( ) ) ;
set1. add ( 129 ) ;
set1. add ( 129 ) ;
Iterator iterator1 = set1. iterator ( ) ;
while ( iterator1. hasNext ( ) ) {
System . out. print ( iterator1. next ( ) + " " ) ;
}
}
}
4. Set接口实现类之三:TreeSet
TreeSet 是 SortedSet 接口的实现类 TreeSet会根据Java比较器中制定的排序规则对其中的元素进行排序,确保集合元素==处于排序状态 == TreeSet中的==元素必须是同一类的对象 ==,保证可以使用Java比较器进行排序 TreeSet底层使用==红黑树 ==存储数据 TreeSet 两种排序方法: 自然排序 和定制排序 。默认情况下,TreeSet 采用自然排序
4.1 TreeSet的自然排序
如果使用TreeSet的空参构造器创建集合对象,会自动调用集合元素所在类的compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按==升序(默认情况)==排列 如果试图把一个对象添加到 TreeSet 时,则==该对象所在类必须实现 Comparable 接口,并实现 Comparable 接口的抽象方法compareTo(Object obj) == Comparable接口的实现类对象通过compareTo(Object obj)方法的返回值来比较大小:
如果当前对象this大于形参对象obj,则返回正整数 如果当前对象this小于形参对象obj,则返回负整数 如果当前对象this等于形参对象obj,则返回零 自然排序的TreeSet,判断两个对象是否相同的标准:集合元素所在类的 compareTo(Object obj) 方法是否返回 0 ,而不再是equals()方法 对于自定义实现类C的对象e1和e2来说,当且仅当 e1.compareTo(e2) == 0 与 e1.equals(e2) 具有相同的boolean值时,类C的自然排序才叫做与 equals() 一致。建议最好使自然排序与 equals() 一致
import org. junit. Test ;
import java. util. Iterator ;
import java. util. TreeSet ;
public class TreeSetTest {
@Test
public void test1 ( ) {
TreeSet set = new TreeSet ( ) ;
set. add ( 34 ) ;
set. add ( - 34 ) ;
set. add ( 43 ) ;
set. add ( 11 ) ;
set. add ( 8 ) ;
Iterator iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. print ( iterator. next ( ) + " " ) ;
}
}
@Test
public void test2 ( ) {
TreeSet set = new TreeSet ( ) ;
set. add ( new User ( "Tom" , 12 ) ) ;
set. add ( new User ( "Jerry" , 32 ) ) ;
set. add ( new User ( "Jim" , 2 ) ) ;
set. add ( new User ( "Jack" , 33 ) ) ;
set. add ( new User ( "Mike" , 12 ) ) ;
Iterator iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
}
}
public class User implements Comparable {
private String name;
private int age;
public User ( String name, int age) {
this . name = name;
this . age = age;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public int getAge ( ) {
return age;
}
public void setAge ( int age) {
this . age = age;
}
@Override
public String toString ( ) {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}' ;
}
@Override
public boolean equals ( Object o) {
if ( this == o) return true ;
if ( o == null || getClass ( ) != o. getClass ( ) ) return false ;
User user = ( User ) o;
if ( age != user. age) return false ;
return name != null ? name. equals ( user. name) : user. name == null ;
}
@Override
public int hashCode ( ) {
int result = name != null ? name. hashCode ( ) : 0 ;
result = 31 * result + age;
return result;
}
@Override
public int compareTo ( Object o) {
if ( o instanceof User ) {
User user = ( User ) o;
int compare = this . name. compareTo ( user. name) ;
if ( compare != 0 ) {
return compare;
} else {
return Integer . compare ( this . age, user. age) ;
}
}
throw new RuntimeException ( "输入的数据类型不一致" ) ;
}
}
4.2 TreeSet的定制排序
当TreeSet集合元素所在类没有实现Comparable接口,或实现了Comparable接口但其重写的compareTo()方法中的排序规则不适合当前操作时,可以使用Comparator接口的实现类对象 进行定制排序 使用方法:将Comparator接口实现类的对象作为参数传递给TreeSet的构造器 ,从而实现定制排序 定制排序的TreeSet,判断两个对象是否相同的标准:当前Comparator比较器对象的 compare(Object o1, Object o2) 方法是否返回 0 ,而不再是equals()方法
面试题:比较HashSet & LinkedHashSet & TreeSet
三者的共同点:
① 都是Set接口的实现类 ② 存储数据的特点相同:单列集合,用来存储一个一个的对象,元素无序、不可重复 三者的不同点:
HashSet:
① Set接口的主要实现类 ② 底层:数组 + 链表 ;底层数组初始容量为16,当使用率超过0.75,就扩容为原来的2倍 ③ 其存储的对象不是按照数组的索引顺序添加的,而是通过调用对象所在类的hashCode()方法 ,根据返回的hashCode值 ,再通过散列函数 计算存储的位置 ④ 判断两个对象是否相同的标准:两个对象调用==equals()方法的返回值是否为true == ⑤ 线程不安全 ⑥ 集合元素可以是==null == LinkedHashSet:
① 是 HashSet 的子类 ② 在HashSet的基础上,使用==双向链表 ==维护元素的次序,可以按照添加的顺序遍历集合元素 ③ 在需要频繁遍历Set元素时,LinkedHashSet的效率比HashSet高 TreeSet:
① SortedSet 接口的实现类 ② 底层使用==红黑树 ==存储数据 ③ TreeSet会根据Java比较器中制定的排序规则对其中的元素进行自然排序或定制排序 ④ TreeSet中的==元素必须是同一类的对象 ==,保证可以使用Java比较器进行排序 ⑤ 自然排序:调用TreeSet的空参构造器创建集合对象,要求集合元素所在类实现Comparable接口,实现compareTo()方法;定制排序:将Comparator接口实现类的对象作为参数传递给TreeSet的构造器,在Comparator接口实现类中实现compare()方法 ⑥ 判断两个对象是否相同的标准:
自然排序的TreeSet:compareTo() 方法是否返回 0 定制排序的TreeSet:compare()方法是否返回0
5. 例题
例1:判断输出。其中Person类中重写了hashCode()和equal()方法
HashSet set = new HashSet ( ) ;
Person p1 = new Person ( 1001 , "AA" ) ;
Person p2 = new Person ( 1002 , "BB" ) ;
set. add ( p1) ;
set. add ( p2) ;
p1. name = "CC" ;
set. remove ( p1) ;
System . out. println ( set) ;
set. add ( new Person ( 1001 , "CC" ) ) ;
System . out. println ( set) ;
set. add ( new Person ( 1001 , "AA" ) ) ;
System . out. println ( set) ;
例2:在List内去除重复数字值,要求尽量简单:将List中的元素放到HashSet中,过滤重复元素
public static List duplicateList ( List list) {
HashSet set = new HashSet ( ) ;
set. addAll ( list) ;
return new ArrayList ( set) ;
}
public static void main ( String [ ] args) {
List list = new ArrayList ( ) ;
list. add ( new Integer ( 1 ) ) ;
list. add ( new Integer ( 2 ) ) ;
list. add ( new Integer ( 2 ) ) ;
list. add ( new Integer ( 4 ) ) ;
list. add ( new Integer ( 4 ) ) ;
List list2 = duplicateList ( list) ;
for ( Object integer : list2) {
System . out. println ( integer) ;
}
}
六、Map接口
1. Map接口概述
Map接口 : 双列集合 ,用来存储一对一对 的,具有==映射关系“key-value” ==的数据Map 中的key和value可以是任何引用类型变量,但key通常为String类型的 Map接口的实现类:HashMap(主要实现类)、LinkedHashMap、TreeMap、Hashtable、Properties
1.1 如何理解Map中的key-value
Map中key-value 的关系就像是函数 y = f(x) 中自变量 x 和因变量 y 之间的关系:key 和 value 之间存在单向一对一关系,即通过指定的 key 总能找到唯一的、确定的 value
Map中的key:无序、不可重复的,使用Set存储所有的key
对于HashMap和LinkedHashMap,key所在的类要重写equals()和hashCode()方法 对于TreeMap,key所在的类要实现Comparable接口或需要使用Comparator的实现类对象 Map中的value:无序、可重复的,使用Collection存储所有的Value
在Map中,key和value实际上都是Entry类的属性,即一个键值对key-value构成一个Entry对象 Map中的Entry对象:无序、不可重复的,使用Set存储所有的Entry对象
2. Map接口实现类之一:HashMap
HashMap是Map接口的主要实现类;线程不安全的 key和value可以是==null == Map中的key:无序、不可重复的,使用HashSet存储所有的key;key所在的类要重写equals()和hashCode() Map中的value:无序、可重复的,使用Collection存储所有的Value;value所在的类要重写equals() 一对key-value构成一个Entry对象;Map中的Entry对象:无序、不可重复的,使用Set存储所有的Entry对象 HashMap判断两个key相等的标准:① 两个key的hashCode()返回值相同 ;② 且通过equals()方法返回true HashMap判断两个 value 相等的标准:两个value通过 equals()方法返回 true
2.1 HashMap底层实现:JDK7.0
2.1.1 HashMap实例化的过程
在实例化HashMap对象时,底层创建一个初始长度为16的一维数组==Entry[] table ,在这个数组中可以存放元素的位置我们称之为 桶 (bucket) ==,每个bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素 每个bucket中存储一个Entry对象,每一个Entry对象有一个Entry类型属性next,用于指向链表中下一个元素,而且新添加的元素作为链表的head,放在数组中,指向原有的元素
HashMap map = new HashMap ( ) ;
2.1.2 向HashMap中添加key-value的过程
当向HashMap中添加(put)一对key-value时,首先调用该key所在类的hashCode()方法 ,返回==该key的hash值 == 根据该hash值,通过某种==散列函数 ==计算当前key-value在底层Entry数组中应当存储的位置**(索引)**
散列函数会根据当前key的hash值和底层Entry数组的长度计算得到当前key-value在数组中的索引 散列函数应当尽可能保证能均匀存储数据,越是散列分布,该散列函数设计的越好 判断在底层Entry数组中,该索引位置上是否已存在Entry对象(key-value对):
若该位置上没有其他Entry对象,则当前key-value以Entry对象的形式添加成功 —> 添加成功:情况1 若该位置上已有其他Entry对象(一个或以链表形式存储的多个Entry对象),则比较当前key与该索引位置上所有Entry对象的key的hash值(hashCode方法的返回值)
若当前key与该索引位置上所有Entry对象的key的hash值都不相同,则当前key-value以Entry对象的形式添加成功 —> 添加成功:情况2 若当前key与该索引位置上某Entry对象的key的hash值相同,则再调用当前key所在类的equals()方法
若equals()返回false,则当前key-value以Entry对象的形式添加成功 —> 添加成功:情况3 若equals()返回true,表明是两个相同的key,则使用当前value替换原有Entry对象的value属性
map. put ( key1, value1) ;
总结:当前要添加的key的hashCode()值 决定了当前Entry对象(包含当前key-value) 在底层Entry数组中存储的位置 ;而==equals()方法 才最终决定了 当前key与该索引位置原有Entry对象的key是否相同 == 对于添加成功的情况2和情况3:当前Entry对象(包含当前key-value)与该索引上原有Entry对象以==链表 ==方式存储
JDK7.0:当前Entry对象(最新添加的对象)作为链表的head,存放到数组中,指向原有的元素 JDK8.0:原有的元素存放在数组中,指向当前Entry对象(最新添加的对象) ——作为链表的last
2.1.3 HashMap底层Entry数组的扩容0
当元素个数超过 当前容量 * 负载因子(threshold值,也叫做临界值),且下一个要添加的索引位置已存在元素,就会进行数组扩容 。loadFactor的默认值(DEFAULT_LOAD_FACTOR)为0.75;底层数组容量(DEFAULT_INITIAL_CAPACITY)默认为16,当HashMap中元素个数超过 16*0.75=12,就会进行首次扩容默认扩容方式:新建一个长度为原数组2倍的新数组(扩容为原来的2倍 ),然后根据Entry对象的key和当前Entry数组的长度重新计算每个元素存放的位置
2.2 HashMap底层实现:JDK8.0的变化
JDK8.0的HashMap,底层的数组是Node[],而不再是Entry[] JDK8.0在实例化HashMap对象时,并没有先创建底层数组,而只是初始化了initialCapacity和loadFactor;当首次调用put()方法添加第一对key-value时,再创建一个初始长度为initialCapacity=16的Node[]数组 JDK7.0:HashMap底层是==数组+链表结构 (即链地址法);JDK8.0:HashMap底层是 数组+链表+红黑树 ==
当底层数组某一索引位置上以链表形式存储的对象个数 > 8 但当前数组长度 < 64 时 ,HashMap会先扩容(扩容后会根据Entry对象的key和当前数组的长度重新计算每个元素存放的位置,可能会减轻链表的负担)当底层数组某一索引位置上以链表形式存储的对象个数 > 8 且当前数组长度 >= 64 时 ,该索引位置上所有数据改用红黑树存储 ——提高查找效率
HashMap map = new HashMap ( ) ;
map. put ( key1, value1) ;
2.3 HashMap源码中的重要常量 & 变量
**DEFAULT_INITIAL_CAPACITY:**HashMap的默认容量(底层数组的默认长度):16 DEFAULT_LOAD_FACTOR: HashMap的默认负载因子(填充因子):0.75TREEIFY_THRESHOLD: Bucket(底层数组的每个索引位置)中链表长度大于该默认值,转化为红黑树:8**MIN_TREEIFY_CAPACITY:**Bucket中的Node对象被树化时最小的HashMap容量(底层数组长度):64 threshold: 扩容的临界值 = 当前HashMap容量 * 负载因子(填充因子)
2.4 面试题:负载因子对HashMap有什么影响
负载因子的大小决定了HashMap的数据密度 负载因子越大,密度越大,数组中的链表越容易长 ,造成查询或插入时的比较次数增多,性能会下降负载因子越小,就越容易触发扩容 ,数据密度也越小,意味着发生==哈希碰撞 ==的几率越小,数组中的链表也就越短,查询和插入时比较的次数也越小,性能会更高,但是会浪费一定的容量空间 。而且经常扩容也比较影响性能,建议初始化预设大一点的容量
哈希碰撞:不同的key,计算出了相同的哈希值(通过散列函数计算在底层数组存储的索引位置相同) HashMap中默认的负载因子(填充因子)为0.75
3. Map接口实现类之二:LinkedHashMap
LinkedHashMap 是 HashMap 的子类 在HashMap的基础上,使用==双向链表 ==来记录添加key-value对的顺序,可以按照添加的顺序遍历 内部类Entry中声明的属性==before 和 after ==专门用来指向当前Entry对象的前一个和后一个Entry对象 在需要频繁遍历Map中的key-value对时,LinkedHashMap的效率比HashMap高
public class MapTest {
@Test
public void test2 ( ) {
Map map1 = new HashMap ( ) ;
map1. put ( 123 , "AA" ) ;
map1. put ( 345 , "BB" ) ;
map1. put ( 12 , "CC" ) ;
System . out. println ( map1) ;
Map map2 = new LinkedHashMap ( ) ;
map2. put ( 123 , "AA" ) ;
map2. put ( 345 , "BB" ) ;
map2. put ( 12 , "CC" ) ;
System . out. println ( map2) ;
}
}
4. Map接口实现类之三:TreeMap
TreeMap 是 SortedMap 接口的实现类 TreeMap会根据Java比较器中制定的排序规则==对所有key进行排序 ,确保Map中的 key-value对处于排序状态 == TreeMap中的==key必须是同一类的对象 ==,保证可以使用Java比较器进行排序 TreeMap底层使用==红黑树 ==存储数据 TreeMap 两种 key 排序方法: 自然排序 和定制排序 。默认情况下,TreeMap 采用自然排序 TreeMap==只能按照key排序,不能按照value排序 ==,因为key-value是单向一对一的关系,指定的 key 能找到唯一的、确定的 value
4.1 TreeMap的自然排序
如果使用TreeMap的空参构造器实例化对象时,会自动调用key所在类的compareTo(Object obj) 方法来比较所有key的大小关系,然后将key-value对按==key升序(默认情况)==排列 TreeMap中的key必须是同一类的对象,且key所在类实现Comparable 接口,重写compareTo(Object obj)方法 Comparable接口的实现类对象通过compareTo(Object obj)方法的返回值来比较大小:
如果当前对象this大于形参对象obj,则返回正整数 如果当前对象this小于形参对象obj,则返回负整数 如果当前对象this等于形参对象obj,则返回零 自然排序的TreeMap,判断两个key是否相同的标准:key所在类实现的 compareTo(Object obj) 方法是否返回 0 ,而不再是equals()方法
public class TreeMapTest {
@Test
public void test1 ( ) {
TreeMap map = new TreeMap ( ) ;
map. put ( new User ( "Tom" , 23 ) , 98 ) ;
map. put ( new User ( "Jerry" , 32 ) , 89 ) ;
map. put ( new User ( "Jack" , 20 ) , 76 ) ;
map. put ( new User ( "Rose" , 18 ) , 100 ) ;
Set entrySet = map. entrySet ( ) ;
Iterator iterator = entrySet. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
}
4.2 TreeMap的定制排序
当TreeMap中的key所在类没有实现Comparable接口,或实现了Comparable接口但其重写的compareTo()方法中的排序规则不适合当前操作时,可以使用Comparator接口的实现类对象 进行定制排序 使用方法:声明一个Comparator接口的实现类,并实现compare(Object o1, Object o2)方法,将该实现类的对象作为参数传递给TreeMap的构造器 ,从而实现定制排序 定制排序的TreeMap,判断两个key是否相同的标准:当前Comparator比较器对象的 compare(Object o1, Object o2) 方法是否返回 0 ,而不再是equals()方法
public class TreeMapTest {
@Test
public void test2 ( ) {
TreeMap map = new TreeMap ( new Comparator ( ) {
@Override
public int compare ( Object o1, Object o2) {
if ( o1 instanceof User && o2 instanceof User ) {
User u1 = ( User ) o1;
User u2 = ( User ) o2;
return Integer . compare ( u1. getAge ( ) , u2. getAge ( ) ) ;
}
throw new RuntimeException ( "输入数据类型不一致" ) ;
}
} ) ;
map. put ( new User ( "Tom" , 23 ) , 98 ) ;
map. put ( new User ( "Jerry" , 32 ) , 89 ) ;
map. put ( new User ( "Jack" , 20 ) , 76 ) ;
map. put ( new User ( "Rose" , 18 ) , 100 ) ;
Set entrySet = map. entrySet ( ) ;
Iterator iterator = entrySet. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
}
}
5. Map接口实现类之四:Hashtable
Map接口的古老实现类,实现原理与HashMap相同,功能也相同,基本不使用了 线程安全的(Hashtable中的方法大多为同步方法),执行效率低 key和value不可以是null
6. Map接口实现类之五:Properties
Properties 是 Hashtable 的子类,其对象用于处理配置文件 由于属性文件里的 key、 value 都是String类型,所以 Properties 里的 key和 value 都是String类型 存取数据时,建议使用==**setProperty(String key,String value)方法和 getProperty(String key)**方法
public class PrioertiesTest {
public static void main ( String [ ] args) throws IOException {
Properties pros = new Properties ( ) ;
FileInputStream fis = new FileInputStream ( "jdbc.properties" ) ;
pros. load ( fis) ;
String name = pros. getProperty ( "name" ) ;
String password = pros. getProperty ( "password" ) ;
System . out. println ( "name = " + name + ", password = " + password) ;
}
}
面试题:比较HashMap & LinkedHashMap & TreeMap & Hashtable & Properties
三者的共同点:
① 都是Map接口的实现类 ② 存储数据的特点相同:双列集合 ,用来存储一对一对 的,具有**映射关系“key-value”**的数据 三者的不同点:
HashMap:
① Map接口的主要实现类;线程不安全的 ② key和value可以是null ③ key:无序、不可重复的;value:无序、可重复的;key-value实际上是HashMap的内部类Entry的属性,实际用Set来存储无序、不可重复的Entry对象 ④ JDK7.0底层是数组+链表 ;JDK8.0底层是数组+链表+红黑树 ⑤ 底层数组存储的Entry对象不是按照数组的索引顺序添加的,而是通过调用key所在类的hashCode()方法 ,根据返回的hash值 ,再通过散列函数 计算存储的位置 ⑥ equals()方法 才最终决定了当前key与该索引位置原有Entry对象的key是否相同 ⑦ 当底层数组某一索引位置上以链表形式存储的对象个数 > 8 且当前数组长度 >= 64 时 ,该索引位置上所有数据改用红黑树存储 LinkedHashMap:
① 是 HashMap 的子类 ② 在HashMap的基础上,使用双向链表 来记录添加key-value对的顺序,可以按照添加的顺序遍历 ③ 在需要频繁遍历Map中的key-value对时,LinkedHashMap的效率比HashMap高 TreeMap:
① SortedMap 接口的实现类 ② 底层使用红黑树 存储数据 ③ TreeMap根据Java比较器制定的规则对对所有key进行排序 ==,确保所有key-value对处于排序状态 ④ TreeMap中的key必须是同一类的对象 ,保证可以使用Java比较器进行排序 ⑤ 自然排序:调用TreeMap的空参构造器创建Map对象,要求key所在类实现Comparable接口,实现compareTo()方法;定制排序:将Comparator接口实现类的对象作为参数传递给TreeMap的构造器,在Comparator接口实现类中实现compare()方法 ⑥ 判断两个对象是否相同的标准:
自然排序的TreeMap:compareTo() 方法是否返回 0 定制排序的TreeMap:compare()方法是否返回0 Hashtable:
① Map接口的古老实现类,实现原理与HashMap相同,功能也相同,基本不使用了 ② 线程安全的(Hashtable中的方法大多为同步方法),执行效率低 ③ key和value不可以是null Properties:
① 是 Hashtable 的子类 ② key和value都是String类型 ③ 常用来处理配置文件
7. Map接口常用方法
7.1 添加、 删除、修改
Object put(Object key,Object value) :将指定key-value添加到(或修改)当前Map对象中**void putAll(Map m):**将另一个Map中的所有key-value对存放到当前Map中 **Object remove(Object key):**移除指定key的key-value对,并返回value **void clear():**清空当前map中的所有数据
@Test
public void test3 ( ) {
Map map = new HashMap ( ) ;
map. put ( "AA" , 123 ) ;
map. put ( 45 , 123 ) ;
map. put ( "BB" , 56 ) ;
map. put ( "AA" , 87 ) ;
System . out. println ( map) ;
Map map1 = new HashMap ( ) ;
map1. put ( "CC" , 123 ) ;
map1. put ( "BB" , 123 ) ;
map. putAll ( map1) ;
System . out. println ( map) ;
Object value = map. remove ( "CC" ) ;
System . out. println ( value) ;
System . out. println ( map) ;
map. clear ( ) ;
System . out. println ( map) ;
}
7.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是否为空,即size()返回值是否为0 **boolean equals(Object obj):**判断当前map和参数对象obj是否相等,形参对象obj应当为对应的Map对象
@Test
public void test4 ( ) {
Map map = new HashMap ( ) ;
map. put ( "AA" , 123 ) ;
map. put ( 45 , 123 ) ;
map. put ( "BB" , 56 ) ;
System . out. println ( map. get ( 45 ) ) ;
System . out. println ( map. get ( 455 ) ) ;
boolean bb = map. containsKey ( "BB" ) ;
System . out. println ( bb) ;
boolean value = map. containsValue ( 123 ) ;
System . out. println ( value) ;
System . out. println ( map. size ( ) ) ;
System . out. println ( map. isEmpty ( ) ) ;
Map map1 = new HashMap ( ) ;
map1. put ( "AA" , 123 ) ;
map1. put ( 45 , 123 ) ;
map1. put ( "BB" , 56 ) ;
System . out. println ( map. equals ( map1) ) ;
}
7.3 元视图操作:遍历Map中的key/value/key-value
当遍历Collection集合中的元素时,可以直接调用iterator()方法,返回一个Iterator迭代器对象遍历集合元素;或使用增强for进行遍历 但是Map接口并没有定义iterator()方法,无法直接使用Iterator迭代器对象进行遍历;也不能直接使用增强for Map中的key使用Set存储,vaule使用Collection存储,Entry对象(key-value对)使用Set存储 ;可以使用以下方法返回所有key、value、Entry对象构成的集合;再调用iterator()方法或使用增强for进行遍历:
**Set keySet():**返回所有key构成的Set集合 **Collection values():**返回所有value构成的Collection集合 **Set entrySet():**返回所有Entry对象(key-value对)构成的Set集合
@Test
public void test5 ( ) {
Map map = new HashMap ( ) ;
map. put ( "AA" , 123 ) ;
map. put ( 45 , 123 ) ;
map. put ( "BB" , 56 ) ;
Set set = map. keySet ( ) ;
Iterator iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. print ( iterator. next ( ) + " " ) ;
}
System . out. println ( ) ;
Collection values = map. values ( ) ;
for ( Object obj : values) {
System . out. print ( obj + " " ) ;
}
System . out. println ( ) ;
Set set1 = map. entrySet ( ) ;
Iterator iterator1 = set1. iterator ( ) ;
while ( iterator1. hasNext ( ) ) {
Object obj = iterator1. next ( ) ;
Map. Entry entry = ( Map. Entry ) obj;
System . out. print ( entry. getKey ( ) + "->" + entry. getValue ( ) + " " ) ;
}
7.4 总结:Map接口常用方法(必须掌握)
增:① put(Object key,Object value);② putAll(Map m) 删:① remove(Object key);② clear() 改:① put(Object key,Object value) 查:① get(Object key) Map中有效元素个数:① size() 遍历:先使用 ① keySet();② values();③ entrySet() 返回所有key、value或Entry对象(key-value对)构成的集合,再通过这些Collection集合 ① 使用Iterator迭代器对象遍历集合元素;② 使用增强for遍历集合元素;
七、Collections工具类的使用
1. Collections工具类概述
Collections是一个操作==Collection的子接口(List、Set)的实现类 和 Map接口的实现类 ==的工具类
Collections 中提供了一系列==静态方法对集合元素进行排序、查询和修改等操作 ,还提供了 对集合对象设置不可变、对集合对象实现同步控制 ==等方法
2. Collections工具类常用方法
2.1 排序操作(只适用于List集合)
**reverse(List list):**反转 List 中元素排列顺序(在底层数组中存储的顺序) **shuffle(List list):**对 List 中元素进行随机排序 **sort(List list):**自然排序:根据元素的自然顺序对指定 List 集合元素按升序排序 **sort(List list, Comparator c):**定制排序:根据指定的Comparator比较器对 List 集合元素排序 **swap(List list, int i, int j):将指定 List 集合索引位置为 i 和 j 的两个元素进行交换
public class CollectionsTest {
@Test
public void test1 ( ) {
List list = new ArrayList ( ) ;
list. add ( 123 ) ;
list. add ( 43 ) ;
list. add ( 765 ) ;
list. add ( - 97 ) ;
list. add ( 0 ) ;
System . out. println ( list) ;
Collections . reverse ( list) ;
System . out. println ( list) ;
Collections . shuffle ( list) ;
System . out. println ( list) ;
Collections . shuffle ( list) ;
System . out. println ( list) ;
Collections . sort ( list) ;
System . out. println ( list) ;
Collections . sort ( list, new Comparator ( ) {
@Override
public int compare ( Object o1, Object o2) {
if ( o1 instanceof Integer && o2 instanceof Integer ) {
Integer i1 = ( Integer ) o1;
Integer i2 = ( Integer ) o2;
return - Integer . compare ( i1, i2) ;
}
throw new RuntimeException ( "输入的数据类型不符" ) ;
}
} ) ;
System . out. println ( list) ;
Collections . swap ( list, 0 , 4 ) ;
}
}
2.2 查找 & 替换操作
**Object max(Collection coll):**根据元素的自然顺序,返回给定集合中的最大元素 **Object max(Collection coll, Comparator comp):**根据Comparator比较器对象定制的顺序,返回给定集合中的最大元素 Object min(Collection coll) Object min(Collection coll, Comparator comp) **int frequency(Collection coll, Object obj):**返回指定集合中指定元素的出现次数 **void copy(List dest, List src):**将src中的元素复制到dest中
src和dest均为List集合 dest.size() >= src.size(),即要求dest中有效元素的个数不能小于src中有效元素的个数 ,否则报异常 解决:List dest = Arrays.asList(new Object[src.size()]); 此时返回的List集合dest中有效元素均为null **boolean replaceAll(List list, Object oldVal, Object newVal):**使用新值替换指定 List 集合的所有指定旧值
public class CollectionsTest {
@Test
public void test2 ( ) {
List list = new ArrayList ( ) ;
list. add ( 123 ) ;
list. add ( 43 ) ;
list. add ( 765 ) ;
list. add ( - 97 ) ;
list. add ( 0 ) ;
System . out. println ( Collections . frequency ( list, 765 ) ) ;
List dest = Arrays . asList ( new Object [ list. size ( ) ] ) ;
System . out. println ( dest. size ( ) ) ;
System . out. println ( dest) ;
Collections . copy ( dest, list) ;
System . out. println ( dest) ;
}
}
2.3 同步控制
Collections 类中提供了多个 synchronizedXxx() 方法,该方法可使==将指定集合包装成线程同步的集合 ,从而可以解决多线程并发访问集合时的 线程安全问题 == 主要用于线程不安全的==ArrayList 和 HashMap ==:synchronizedList()和synchronizedMap()
public class CollectionsTest {
@Test
public void test3 ( ) {
List list = new ArrayList ( ) ;
list. add ( 123 ) ;
list. add ( 43 ) ;
list. add ( 765 ) ;
list. add ( - 97 ) ;
list. add ( 0 ) ;
List list1 = Collections . synchronizedList ( list) ;
}
}
3. 面试题:Collection & Collections 的区别
Collection接口:
存储单列数据的集合接口,用来存储一个一个的对象 但JDK不提供Collection接口的直接实现类,而是提供更具体的子接口,如:List和Set接口 Collections工具类:是一个操作Collection的子接口(List、Set)的实现类和Map接口的实现类的工具类