Java中的List集合、Set集合、Map集合
在看着视频写题目的时候发现视频对集合的讲解都是概念性的,没有实操,我看不懂,所以查了很多资料才知道这集合该怎么写怎么用
文章目录
概念
在Java程序中可以通过数组来保存多个对象,但在某些情况下开发人员无法确定需要保存的对象的个数,这时数组就不再适用了,原因是数组的长度是固定不变的。为了在程序中保存这些数据不确定的对象,JDK中提供了一系列特殊的类,这些类可以存储任意类型的对象,更重要的是它们的长度是可变的,在Java中这些类统称为集合。
Java集合与数组不同的还有,Java集合中不能存放基本类型数据,而只能存放对象的引用。
集合类都位于java.util包中,在使用这些类的时候,首先要导入这些包,否则会出现异常。
Java集合主要分为4种类型:Set(集)、List(列表)、Queue(队列)、Map(映射)
集合按照它的存储结构可以分为两大类:单列集合Collection和双列集合Map。
单列集合Collection:是单列集合的根接口,主要用于存储一系列符合某种规则的元素,它有两个重要的子接口List和Set。List接口的特点是元素有序可重复。Set接口的特点是元素无序不可重复。List接口的主要接口实现类有两个:ArrayList和LinkedList。而Set接口的主要实现类也有两个:HashSet和TreeSet。
双列集合Map:是双列集合的根接口,用于存储具有键(Key)、值(Value)映射关系的元素(键-值对),每个元素都包含一个键-值对,在使用该集合时可以通过指定的键找到对应的值。Map接口的主要接口实现类有两个:HashMap和TreeMap。
Collection和Iterator接口
Collection接口中可声明的适用于Java集合(只包括Set、List、Queue)的通用方法:
方法 | 描述 |
---|---|
boolean clear() | 向集合中加入一个对象的引用 |
void clear() | 删除集合中的所有对象,即不再持有这些对象的引用 |
boolean contains(Object o) | 判断在集合中是否持有特定对象的引用 |
boolean iaEmpty() | 判断集合是否为空 |
Iterator iterator() | 返回一个Iterator对象,可用它来遍历集合中的元素 |
boolean remove(Object o) | 从集合中删除一个对象的引用 |
int size() | 返回集合中元素的数目 |
Object[] toArray() | 返回一个数组,该数组包含集合中的所有元素 |
<T>T[] toArray(T[] a) | 返回一个数组,该数组包含集合中的所有元素 |
Set接口、List接口和Queue接口都继承了Collection接口,而Map接口没有继承Collection接口,因此可以对Set对象、List对象和Queue调用以上方法,但是不能对Map对象调用以上方法。
Collection接口的iterator()和toArray()方法都用于获得集合中的所有元素,前者返回一个Iterator对象,后者返回一个包含集合中所有元素的数组。
Iterator接口隐藏底层集合的数据结构,向客户程序提供了遍历各种类型的集合的统一接口。
Iterator接口中声明的方法:
- hasNext():判断集合中的元素是否遍历完毕,如果没有,就返回true
- next():返回下一个元素
- remove():从集合中删除由next()方法返回的当前元素
注意:如果集合中的元素没有排序,Iterator遍历集合中元素的顺序是任意的,并不一定与向集合中加入元素的顺序一致。
当通过Collection集合的iterator()方法得到一个Iterator对象后,如果当前线程或其他线程接着又通过Collection集合的一些方法对集合进行了修改操作(调用当前Iterator对象的remove()方法来修改集合除外),接下来访问这个Iterator对象的next()方法会导致java.util.ConcurrentModificationException运行时异常。
方法
addAll()方法
addAll(Collection<? extends E> col)方法用来将指定集合中的所有对象添加到该集合中。
如果对该集合进行了泛化,则要求指定集合中的所有对象都符合泛化类型,否则在编译程序时将抛出异常,入口参数中的<? extends E>就说明了这个问题,其中E为用来泛化的类型。
使用addAll()方法向集合中添加对象:
/*在下面的例子中:
1、首先通过add(E obj)方法添加两个对象(a和b)到list集合中
2、然后依次通过addAll(Collection<? extends E> col)方法和add(E obj)方法将集合list中的
所有对象(包括对象c)添加到集合list2中
3、又通过iterator()方法序列化集合list2,获得了一个Iterator型实例it
4、因为集合list和list2中的所有对象均为String类型,所以将实例it也泛化为String类型,所以可以
通过next()方法得到的对象直接赋值给String类型对象str,否则就需要先执行强制类型转换
要特别注意,由于Collection是接口,所以无法实例化,而ArrayList类是Collection接口的间接实现类,
所以可以通过ArrayList类实例化
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test01 {
public static void main(String[] args) {
String a = "A";
String b = "B";
String c = "C";
Collection<String> list = new ArrayList<String>();
list.add(a); //通过add(E obj)方法添加指定对象到集合中
list.add(b);
Collection<String> list2 = new ArrayList<String>();
//通过addAll(Collection<? extends E> col)方法添加指定集合中的所有对象到该集合中
list2.addAll(list);
list2.add(c);
Iterator<String> it = list2.iterator();//通过Iterator()方法序列化集合中的所有对象
while(it.hasNext()){
String str = it.next(); //因为实例it进行了泛化,所以不需要进行强制类型转换
System.out.println(str);
}
}
}
removeAll()方法
removeAll(Collection<?> col)方法用来从该集合中移除同时包含在指定集合中的对象,与retainAll()方阿飞正好相反。返回值为bolean型,如果存在符合移除条件的对象则返回true,否则返回false。
使用removeAll()方法从集合中移除对象:
/*
1、首先分别创建两个集合list和list2,在list中包含了对象a和对象b,在集合list2中包含了对象b和对象c
2、然后从集合list中移除同时包含在集合list2中的对象
3、最后遍历集合list,输出true说明存在符合移除条件的对象,输出false说明不存在符合移除条件的对象
*/
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test01 {
public static void main(String[] args) {
String a = "A";
String b = "B";
String c = "C";
Collection<String> list = new ArrayList<String>();
list.add(a); //通过add(E obj)方法添加指定对象到集合中
list.add(b);
Collection<String> list2 = new ArrayList<String>();
list2.add(b);
list2.add(c);
//通过removeAll()方法从该集合中移除同时包含在指定集合中的对象,并获得返回信息
boolean isContains = list.removeAll(list2);
System.out.println(isContains);
Iterator<String> it = list.iterator();
while (it.hasNext()){
String str = it.next();
System.out.println(str);
}
}
}
containsAll()方法
containsAll(Collection<?> col)方法用来查看在该集合中是否存在指定集合中的所有对象。返回值为boolean型,如果存在则返回true,否则返回false。
使用containsAll()方法查看在集合list中是否包含集合list2中的所有对象:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test01 {
public static void main(String[] args) {
String a = "A";
String b = "B";
String c = "C";
Collection<String> list = new ArrayList<String>();
list.add(a); //通过add(E obj)方法添加指定对象到集合中
list.add(b);
Collection<String> list2 = new ArrayList<String>();
list2.add(b);
list2.add(c);
//通过containsAll()方法查看在该集合中是否存在指定集合中的所有对象,并获得返回信息
boolean isContains = list.containsAll(list2);
System.out.println(isContains);
}
}
/*
程序执行后,控制台输出false,说明在集合list(a,b)中不包含集合list2(b,c)中的所有对象
*/
toArray()方法
toArray(T[] t)方法用来获取一个包含所有对象的指定类型的数组。
toArray(T[] t)方法的入口参数必须为数组类型的实例,并且必须已经被初始化,它用来指定要获得的数组的类型,如果调用toArray(T[] t)方法的实例进行了泛化,还要求入口参数的类型必须符合泛化类型。
使用toArray()方法获得一个包含所有对象的指定类型的数组:
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class Test01 {
public static void main(String[] args) {
String a = "A";
String b = "B";
String c = "C";
Collection<String> list = new ArrayList<String>();
list.add(a); //通过add(E obj)方法添加指定对象到集合中
list.add(b);
list.add(b);
list.add(c);
String strs[] = new String[1];//创建一个String类型的数组
String strs2[] = list.toArray(strs);//获得一个包含所有对象的指定类型的数组
for (int i=0;i<strs2.length;i++){
System.out.println(strs2[i]);
}
}
}
/*
程序执行后,将会生成一个String类型的数组strs2,其元素就是集合list的所有对象
*/
List集合
List集合为列表类型,以线性方式存储对象。
List集合包括List接口以及List接口的所有实现类。
List集合中的元素允许重复,各元素的顺序就是对象插入的顺序。
和Java数组类型一样,用户通过使用索引来访问List集合中的元素。
List接口的常用实现类有ArrayList与LinkedList
- ArrayList类实现了可变的数组,允许保存所有元素,包括null,并可以根据索引位置对集合进行快速的随机访问;缺点是向指定的索引位置插入ui想或删除对象的速度较慢
- LinkedList类采用链表结构保存对象。这种结构的有点是便于向集合中插入和删除对象,需要向集合中插入,删除对象时,使用LinkedList类实现的List集合的效率较高;但对于随机访问集合中的对象,使用LinkedList类实现List集合的效率较低
List接口继承了Collection接口并定义一个允许重复项的有序集合。List接口使用一个开始于0的下标,元素可以通过它们在列表中的位置被插入和访问。一个列表可以包含重复元素。除了由Collection定义的方法之外,List还定义了一些它自己的方法:
方法 | 说明 |
---|---|
void add(int index,Object obj) | 将obj插入调用列表,插入位置的下标由index传递。任何已存在的,在插入点以及插入点之后的元素将前移,因此,没有元素被覆写 |
Boolean addAll(int index,Collection c) | 将c中的所有元素插入到调用列表中,插入点的下标由index传递。在插入点以及插入点之后的元素将前移,因此,没有元素被复写。如果调用列表改变了,则返回true,否则返回false |
Object get(int index) | 返回存储在调用类集内指定下标处的对象 |
int lastIndexOf(Object obj) | 返回调用列表中obj的第一个实例的下标。如果obj不是列表中的元素,则返回-1 |
int lastIndexOf(Object obj) | 返回调用列表中obj的最后一个实例的下标。如果obj不是列表中的元素,则返回-1 |
ListIterator listIterator() | 返回调用列表开始的迭代程序 |
ListIterator listIterator(int index) | 返回调用列表在指定下标处开始的迭代程序 |
Object remove(int indx) | 删除调用列表中index位置的元素并返回删除的元素。删除后,列表被压缩。也就是说,被删除的元素后面的元素的下标-1 |
Object set(int index,Object obj) | 用obj对调用列表内由index指定的位置进行赋值 |
List sbuList(int start,int end) | 返回一个列表,该列表包括了调用列表中从start到end-1的元素。返回列表中的元素也被调用对象引用 |
注意:当类集不能被修改时,其中的几种方法将引发UnsupportedOperationException异常。当一个对象与另一个不兼容,例如将一个不兼容的对象加入一个类集中时,将产生ClassCastException异常。
例:在项目中创建类Gather,在主方法中创建集合对象,通过Math类的random()方法随机获取集合中的某个元素,然后移除数组中索引位置是“2”的元素,最后遍历数组
import java.util.ArrayList;
import java.util.List;
public class Gather{
public static void main(String[] args){
List<String> list = new ArrayList<>();
//向数组中添加元素
list.add("a");
list.add("b");
list.add("c");
//获得0-2之间的元素
int i = (int)(Math.random()*list.size());//Math类的random()方法可以获得一个0.0-1.0的随机数
System.out.println("随机获取数组中的元素:" + list.get(i));
//将指定元素从数组中移除
list.remove(2);
System.out.println("将索引是“2”的元素从数组移除后,数组中的元素是:");
//循环遍历集合
for(int j=0;j<list.size();j++){
System.out.println(list.get(j));
}
}
}
运行结果
ArrayList集合
ArrayList类扩展AbstractList并执行List接口。
本质上,ArrayList是对象引用的一个边长数组。也就是说,ArrayList能够动态地增加或减小其大小。数组列表以原始大小被创建。当超过了它的大小时,类集就会自动这种增大。当有对象被删除时,数组就可以缩小。
ArrayList有以下构造方法:
- ArrayList(),建立一个空的数组列表
- ArrayList(Collection c),建立一个数组列表,该数组列表由类c中的元素初始化
- ArrayList(int capacity),建立一个数组列表,该数组有指定的初始容量(capacity),容量是用于存储元素的基本数组的大小。
当元素被追加到数组列表上时,容量就会增加
关于ArrayList的简单例子:
首先创建一个数组列表,接着添加String类型的对象,接着列表被显示出来。将其中一些元素删除后,则再一次显示列表
public static void main(String[] args) {
ArrayList al = new ArrayList();//创建一个ArrayList对象
System.out.println("al的初始化大小:" + al.size());
//向ArrayList对象中添加元素
al.add("a");
al.add("b");
al.add("c");
al.add("d");
al.add(1,"A");//把A添加到ArrayList对象的第2个位置
System.out.println("al加入元素之后的大小:" + al.size());
System.out.println("al的内容:" + al);
//从ArrayList对象中移除元素
al.remove("b");
al.remove(3);//移除下标为3的元素
System.out.println("al删除元素之后的大小;" + al.size());
System.out.println("al的内容:" + al);
}
运行结果;
从上面的程序我们可以注意到,开始的时候al是空的,当添加元素后,它的大小增加了,当移除元素后,它的大小又会变小了。
LinkedList集合
LinkedList类扩展了AbstractSequentialList类并实现了List接口。它提供了一个链接列表的数据结构
LinkedList有两个构造方法;
- LinkedList():建立一个空的链接列表
- LinkedList(Collection c)建立一个链接列表,该链接列表由类c中的元素初始化
还有一些其他有用的方法:
- void addFirst(Object obj):在列表头增加元素
- void addLast(Object obj):在列表尾增加元素
- Object getFirst():获得第一个元素
- Object getLast():获得最后一个元素
- Object removeFirst():删除第一个元素
- Object removeLast():删除最后一个元素
Iterator集合
Iterator是在进行集合输出的过程中最常见的一种输出接口,这个接口的定义为:
public static void main(String[] args) {
List<String> all = new ArrayList<String>();
all.add("a");
all.add("b");
all.add("c");
Iterator<String> iter = all.iterator();
while (iter.hasNext()){
String str = iter.next();
System.out.println(str);
}
}
Set集合
Set集合中的对象不按特定方式排序,只是简单地把对象加入集合中,但是Set集合中不能包含重复对象。Set集合由Set接口和Set接口的实现类组成。Set接口继承了Collection接口,因此也包括Collection接口的所有方法。
HashSet集合
HashSet扩展AbstractSet并且实现Set接口。
HashSet的构造方法定义:
- HashSet():构造一个默认的散列集合
- HashSet(Collection c):用c中的元素初始化散列集合
- HashSet(int capacity):用capacity初始化散列集合的容量
- HashSet(int capacity,float fillRatio):用参数初始化散列集合的容量和填充比(也称为加载容量)
注意:填充比必须介于0.0与1.0之间,它决定在散列集合向上调整大小之前有多少能被充满。具体来说即使当元素的个数大于散列集合容量容量乘以它的填充比时,散列集合被扩大。对于没有给出填充比的构造方法,默认使用0.75
HashSet没有定义任何超类和接口的其他方法
需要注意的是,散列集合并不能确定其他元素的排列顺序,通常无法干预排序集合的创建。如果需要排序,另一种类集——TreeSet将是一个更好的选择
HashSet类的使用:
public static void main(String[] args) {
//创建HashSet对象
HashSet hs = new HashSet();
//加入元素到HastSet中
hs.add("a");
hs.add("d");
hs.add("c");
hs.add("e");
hs.add("b");
hs.add("f");
System.out.println(hs);
}
TreeSet集合
TreeSet为使用树来进行存储的Set接口提供了一个工具,对象按升序存储,访问和检索是很快的。在存储了大量的需要进行检索的排序信息的情况下,TreeSet是一个很好的选择。
TreeSet的构造方法:
- TreeSet():构造一个空的树集合,该树集合将根据其元素的自然顺序按升序排序
- TreeSet(Collection c):构造一个包含了集合c的元素的树集合
- TreeSet(Comparator comp):构造一个空的树集合,它按照由comp指定的比较方法进行排序
- TreeSet(SortedSet ss):构造一个包含了已排序集合ss的元素的树集合
TreeSet的使用:
public static void main(String[] args) {
//创建HashSet对象
TreeSet ts = new TreeSet();
//加入元素到HastSet中
ts.add("a");
ts.add("d");
ts.add("c");
ts.add("e");
ts.add("b");
ts.add("f");
System.out.println(ts);
}
运行结果:
Map集合
Map集合么有继承Collection接口,它提供的是键到值的映射。Map中不能包括相同的键,每个键只能一个值。键还决定了存储对象在映射中的存储位置,但不是由键对象本身决定的,而是通过一种散列技术进行处理,产生一个散列码的整数值。散列码通常用作偏移量,这个偏移量对应分配给映射的内存区域的起始位置,从而确定存储对象在映射中的存储位置。Map集合包括Map接口以及Map接口的所有接口实现类。
Map接口映射唯一关键字到值,关键字(key)是以后用于检索值的对象。给定一个关键字和一个值,可以存储这个值到一个Map对象中。当这个值被存储以后,就可以使用它的关键字来检索它。
Map的方法:
方法 | 说明 |
---|---|
void clear() | 从调用映射中删除所有的键-值对 |
boolean containsKey(Object k) | 如果调用映射中包含了作为键的k,则返回true,否则返回false |
boolean containsValue(Object v) | 如果映射中包含了作为值的v,则返回true,否则返回false |
Set entrySet() | 返回映射中包含的项的集合(Set)。该集合包含了类型Map.Entry的对象。这个方法为调用映射提供了一个集合“视图” |
Boolean equals(Object obj) | 如果obj是一个映射并包含相同的输入,则返回true,否则返回false |
Object get(Object k) | 返回与键k相关联的值 |
int hashCode() | 返回调用映射的散列码 |
boolean isEmpty | 如果调用映射是空的,则返回true,否则返回false |
Set keySet() | 返回调用映射中包含的键的集合(Set)。这个方法为调用映射的关键字提供了一个集合“视图” |
Object put(Object k,Object v) | 将一个输入加入调用映射,改写原先与该键相关联的值。键和值分别为k和v。如果键已经不存在了,则返回null;否则,返回原先与键相关联的值 |
void putAll(Map m) | 将所有来自m的输入加入调用映射 |
Object remove(Object k) | 删除键等于k的输入 |
int size() | 返回映射中的键-值对的个数 |
Collection values() | 返回一个映射中包含的值的类集。这个方法为映射中的值提供了一个类集“视图”。映射循环使用两个基本操作:get()和put()。使用put()方法可以将一个指定了键的值加入映射。为了得到值,可以用键作为参数来调用get()方法,返回该值 |
当调用的映射中没有元素存在时,其中的几个方法会引发一个NoSuchElementException异常。
而当对象与映射中的元素不兼容时,则会引发一个ClassCastException异常。
如果试图使用映射不允许使用的null对象,则会引发一个NullPointerException异常。
当试图改变一个不允许修改的映射时,则会引发一个UnsupportedOperationException异常。