有人说用数组就好了为什么还要学集合呢?
1、Java数组和集合区别
在Java中,数组是一种用于存储同类型数据的有序集合,其最显著的特点是在创建时必须明确指定数组的长度。这一要求是基于数组在内存中的连续存储机制,即数组一旦被分配空间,就会占据一段连续的内存区域,其大小(即能存储的元素数量)也就固定下来。这样做是为了确保程序在运行时能够准确地知道数组的大小,从而安全地访问数组中的元素,避免越界错误。
然而,在实际开发中,经常遇到需要处理的数据量并非固定不变的情况。例如,管理一个公司的员工信息时,员工数量会随着招聘和离职而变动。在这种情况下,使用固定长度的数组就显得不够灵活,因为每次员工数量的变化都可能需要重新分配一个更大或更小的数组,并手动复制数据,这不仅效率低下,还容易出错。
为了解决这个问题,Java引入了集合框架(Collections Framework),它提供了一系列接口和类,用于存储和操作数量可变的数据集合。与数组不同,集合类可以动态地调整其大小以容纳更多或更少的元素,无需手动进行数组的重新分配和数据复制。这使得集合类在处理不确定大小的数据集时更加灵活和高效。
Java集合类与数组的主要区别在于:
- 长度可变:集合类的大小可以根据需要动态变化,而数组的大小在创建时就已确定且不可更改。
- 存储类型:集合类只能存储对象的引用,不能直接存储基本数据类型(如int、double等),但可以通过封装类(如Integer、Double)来间接存储。数组则可以直接存储基本数据类型或对象的引用。
- 功能丰富:集合类提供了丰富的操作方法,如添加、删除、查找、遍历等,使得数据的处理更加便捷。而数组虽然也支持遍历和访问元素,但在添加和删除元素方面则相对复杂和低效。
因此,Java集合类被广泛应用于需要处理不确定大小数据集的场景中,如用户列表、商品库存、消息队列等,它们为Java程序提供了强大的数据管理能力。
2、Java集合有哪些
1.Java集合框架提供了多种不同的数据结构来实现集合,主要包括:
- List:有序集合,允许包含重复元素。List接口的实现类(如ArrayList、LinkedList)通常使用数组或链表来存储元素。
- Set:无序集合,不允许包含重复元素。Set接口的实现类(如HashSet、TreeSet)通常使用哈希表或树等数据结构来实现元素的存储和去重。
- Map:键值对映射集合,键(Key)是唯一的,值(Value)可以重复。Map接口的实现类(如HashMap、TreeMap)通常使用哈希表、树或链表等数据结构来实现键值对的存储和查找。
2.Java中集合框架层次结构
Java集合框架为我们提供了处理一组对象的标准方式,这些标准在集合框架中被设计为一系列的接口。同时,集合框架还提供了这些接口的实现类。下图是Java集合框架的层次结构。图中蓝色箭头表示的是实现接口,红色箭头表示的是继承类。
上图中的蓝色箭头表示的是接口,接口具体包括:
- Collection接口:是集合层次中的一个根接口,Collection 表示一组对象,这些对象也称为 Collection 的元素。一些Collection 允许有重复的元素,而另一些则不允许。一些Collection 是有序的,而另一些则是无序的,JDK 不提供此接口的任何直接实现,Collection接口是List接口和Set接口的父接口。
- List接口:有序、可包含重复元素的Collection,通常被称为链表,此接口的用户可以对列表中每个元素的插入位置进行精确地控制,用户也可以根据元素的整数索引访问元素,并搜索链表中的元素。
- Set接口:无序、不包含重复元素的Collection,通常被称为数学意义上集合。
- Map接口:组织映射数据,表示很多数据,每个数据都会包含两部分,一部分是数据,另一部分是键,每个数据称为键/值对(key/value)。
上图中的实线框表示是实际要使用的类,这些类比较多,也是我们需要重点掌握的。
3、Collection接口
Collection接口用来存放一组称为元素的对象,一个Collection中可以放不同类型的数据,它是Set接口和List接口的父接口。其中元素是否有特定的顺序以及是否允许重复,取决于它的实现类。
(1) 子接口Set:无序的集合,不允许重复。Set接口的实现类:HashSet
(2) 子接口List:有序的集合,允许重复。List接口的实现类:ArrayList、LinkedList
1.Collection接口通用方法
最顶层接口就是Collection,表示一个集合。因为是接口,所以主要考虑它的方法,这个接口中定义的方法是所有实现该接口的类都应该实现的。因为Collection描述的是集合,所以它的方法都是与集合操作相关的方法。
(1)向集合中添加对象的方法
可以添加一个,也可以添加多个,添加多个也就是把另外一个集合的元素添加进来,下面的两个方法是添加对象的方法:
public boolean add(Object o)//向集合中添加参数指定的元素。
public boolean addAll(Collection c)//向集合中添加参数指定的所有元素。
(2)从集合中删除元素的方法
可以删除一个,可以删除多个,还可以删除所有的元素,此外还有一个特殊的,删除某些元素之外的所有元素,所以对应的方法也有四个:
public boolean remove(Object o) //删除指定的某个元素。
public boolean removeAll(Collection c)//删除指定的多个元素。
public void clear() //删除所有的元素。
public boolean retainAll(Collection c)
//只保留指定集合中存在的元素,其他的都删除,相当于取两个集合的交集。
(3)判断集合中元素的方法
public boolean isEmpty()//用于判断集合是否是空的。
public boolean contains(Object o) //判断是否包含指定的元素。
public boolean containsAll(Collection c) //判断是否包含指定的多个元素。
public int size()//用于获取集合中元素的个数。
(4)与其它类型的对象进行转换的方法
public Iterator iterator()//转换成迭代器,方便集合中元素的遍历。
public Object[] toArray()//返回一个包含所有元素的对象数组,也是方便集合中元素的遍历。
通常在管理集合的过程中,使用集合本身提供的方法,但是遍历集合最好先转换成迭代器或者数组,这样访问比较方便,并且效率比较高。
(5)比较通用的方法
public boolean equals(Object o)//判断是否与另外一个对象相同。
public int hashCode()//返回集合的哈希码。
4、List接口及其实现类
List接口继承了Collection接口,用来包含一组有序有重复的对象。
与数组相似,List中的每个元素都对应一个索引,记载其在容器中的位置,可以根据序号存取容器中的元素。List是一种长度可变的线型集合,集合中的元素是有序的,所以多了一些与顺序相关的方法。这里只介绍增加的方法。
(1)在指定的位置上添加元素
public void add(int index , Object o)
// 第一个参数表示要添加的元素的位置,从0开始。
public boolean addAll(int index , Collection c)
//第一个参数表示位置,如果不指定位置,默认在最后添加。
(2)删除指定位置的元素
public Object remove(int index) // 参数用于指定要删除的元素的位置。
(3)获取某个元素或者获取某些元素
//获取指定位置的元素。
public Object get(int index)
//获取从fromIndex到toIndex这些元素,包括fromIndex,不包括toIndex。
public List subList(int fromIndex,int toIndex)
(4)查找某个元素
// :查找元素在集合中第一次出现的位置,并返回这个位置,如果返回值为-1,表示没有找到这个元素。
public int indexOf(Object o)
//:查找元素在集合中最后一次出现的位置。
public int lastIndexOf(Object o)
(5)修改元素的方法
//用第二个参数指定的元素替换第一个参数指定位置上的元素。
public Object set(int index , Object o)
(6)转换成有顺序的迭代器:
// 把所有元素都转换成有顺序的迭代器。
public ListIterator listIterator()
//从index开始的所有元素进行转换。
public ListIterator listIterator(int index)
List有三种主要的实现类
- ArrayList
- LinkedList
- Vector
1.ArrayList类
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
List接口的长度可变更的数组方式实现,实现所有可选列表操作。
ArrayList是List接口的类似于数组的实现,建议通过下标管理元素。
ArrayList是一种动态的长度可自动变更的数组。
原则上所有的对象都可以加入到ArrayList里,包括null。
但通常为了使用方便,一般可以通过泛型<T>限定加入到ArrayList中的元素类型,以保证加入指定类型的元素。
该类的构造方法有3种重载形式:
ArrayList() // 构造一个初始容量为10的空的链表;
// 使用1个已经存在的集合构造一个链表,集合中的元素在新的链表中的顺序由集合的iterator()方法决定。
ArrayList(Collection< ? extends E> c)
//构造一个由参数指定初始化空间大小的链表。
ArrayList(int initialCapacity)
// 实例化一个空列表,添加元素
ArrayList list1 = new ArrayList();
list1.add("user1");
list1.add("user2");
// 实例化一个列表,并填充list1中的现有元素
ArrayList list2 = new ArrayList(list1);
// 实例化一个列表,初始容量为8。
ArrayList list3 = new ArrayList(8);
ArrayList其它的主要方法还包括:
(1)向ArrayList中添加对象
可以在最后添加,也可以在指定的位置添加对象。可以添加一个,可以添加多个,添加多个也就是把另外一个集合的元素添加进来。
//在链表的最后添加参数指定的元素。
public boolean add(Object o)
//第一个参数表示要添加的元素的位置,从0开始。
public void add(int index,Object o)
//在List对象最后添加参数指定的所有元素。
public boolean addAll(Collection c)
//第一个参数表示位置,如果不指定位置,默认在最后添加。
public boolean addAll(int index,Collection c)
下面的代码展示了这些方法的应用:
// 实例化一个空列表,添加元素
ArrayList list1 = new ArrayList();
list1.add("user1");
list1.add("user2");
// 实例化一个列表,并填充list1中的现有元素
ArrayList list2 = new ArrayList(list1);
// 实例化一个列表,初始容量为8。
ArrayList list3 = new ArrayList(8);
list1.add("user3");
list1.addAll(list2);
list1.add(0,"user0");
运行结果:
list1 = [user0, user1, user2, user3, user1, user2]
(2)删除特定的元素
可以删除一个,可以删除多个,还可以删除所有的元素,此外还有一个特殊的,删除某些元素之外的所有元素,所以对应的方法也有五个:
//参数用于指定要删除的元素的位置。
public Object remove(int index)
//删除指定的某个元素。
public boolean remove(Object o)
//删除指定的多个元素。
public boolean removeAll(Collection c)
//只保留指定集合中存在的元素,其他的都删除,相当于取两个集合的交集。
public boolean retainAll(Collection c)
//删除所有的元素。
public void clear()
下面的代码删除了user1:
list1.remove("user1");
注意:这里只删除了第一个出现的user1。
(3)获取某个元素或者获取某些元素
可以获取某个位置的单个元素,也可以获取多个元素。
//获取指定位置的元素。
public Object get(int index)
//获取从fromIndex到toIndex这些元素,包括fromIndex,不包括toIndex。
public List subList(int fromIndex,int toIndex)
如果list1 = [user0, user1, user2, user3, user1, user2],那么获取第三个元素可以使用下面的代码:
String str = list1.get(2);
结果str = user2
(4)查找某个元素
可以根据位置查找集合中的对象,也可以判断结合中是有对象,以及是否是空的,元素的个数等。
//查找元素在集合中第一次出现的位置,并返回这个位置,如果返回值为-1,表示没有找到这个元素。
public int indexOf(Object o)
//查找元素在集合中最后一次出现的位置。
public int lastIndexOf(Object o)
//用于判断集合是否是空的。
public boolean isEmpty()
// 判断是否包含指定元素
public boolean contains(Object o)
//判断是否包含指定的多个元素。
public boolean containsAll(Collection c)
如果 list1 = user0, user2, user3, user1, user2,下面的代码用于查找user1第一次出现和最后一次次出现的位置。
System.out.println(list1.indexOf("user2"));
System.out.println(list1.lastIndexOf("user2"));
得到的结果:
1
4
(5)修改元素的方法
//用第二个参数指定的元素替换第一个参数指定位置上的元素。
public Object set(int index,Object o)
如果list1= user0, user2, user3, user1, user2, 下面的代码把第二个元素修改user4:
list1.set(1,"user4");
修改后的元素为:
list1 = user0, user4, user3, user1, user2。
(6)转换成其它对象
//把所有元素都转换成有顺序的迭代器。
public ListIterator listIterator()
//从index开始的所有元素进行转换。
public ListIterator listIterator(int index)
//转换成迭代器,方便集合中元素的遍历。
public Iterator iterator()
//转换成数组,也是方便集合中元素的遍历。
public Object[] toArray()
(7)获取集合中元素的个数
//用于获取集合中元素的个数。
public int size()
如果list1= user0, user2, user3, user1, user2, 则list1.size 返回5。
(8)ArrayList的遍历
可以采用下面的3种方法进行遍历。
方法1:原始的for循环方法
for(int i=0;i<list1.size();i++){
System.out.println(list1.get(i));
}
方法2:转换为数组后,遍历数组
Object o[] = list1.toArray();
for(int i=0;i<o.length;i++){
String temp = (String)o[i];
System.out.println(temp);
}
方法3:使用迭代器进行
Iterator i = list1.iterator();
while(i.hasNext()){
String temp = i.next();
System.out.println(temp);
}
通常在管理集合的过程中使用集合本身提供的方法,但是遍历集合最好先转换成迭代器或者数组,这样访问比较方便,并且效率比较高。
ArrayList的使用。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ListDemo {
public static void main(String args[]) {
List li = new ArrayList();
li.add("one");
li.add("two");
li.add(3);
li.add(new Float(4.0F));
li.add("two");
li.add(new Integer(3));
System.out.println(li);
}
}
程序运行结果:
[one, two, 3, 4.0, two, 3]
5、List集合遍历
1、Iterator和ListIterator接口
1)Iterator接口
Iterator对象称作迭代器,用来方便的实现对容器内的元素进行遍历操作。
所有实现了Collection接口的集合类都有一个iterator()方法,该方法返回一个实现了Iterator接口的对象。Iterator对象实现了统一的一个用来遍历Collection中对象的方法。
Iterator是为遍历而设计,能够从集合中取出元素和删除元素,但是没有添加元素的功能。Iterator的功能上比较简单,使用时,只能单向移动。
Iterator接口的定义如下:
package java.util;
public interface Iterator {
// 判断迭代器中是否有下一个元素,如果有,返回true,否则返回false。
boolean hasNext();
// 用于得到下一个元素,返回值是Object类型,需要强制转换成自己需要的类型。
Object next();
// 用于删除元素。
void remove();
}
关于迭代器的使用,通常是在得到它之后,对它进行遍历,如下:
public static void main(String[] args) {
List list = new ArrayList();
// 为List赋值
for (int i = 0;i < 6;i++){
list.add(new Integer(i));
}
Iterator iterator = list.iterator(); //生成一个可操作list集合的迭代器
while(iterator.hasNext()){ //循环判断每次迭代器中的list是否还有数据进行迭代
System.out.println(iterator.next());//输出当前迭代器中的数据
}
}
2)ListIterator接口
ListIterator接口是Iterator接口的子接口,相对于Iterator,它提供了更多方法。
使用ListIterator遍历元素
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListIteratorExercise {
public static void main(String args[]) {
List list = new ArrayList();
// 为List赋值
for (int i = 0;i < 6;i++) {
list.add(new Integer(i));
}
//遍历输出每一个元素
ListIterator lit = list.listIterator();
while (lit.hasNext()) {
System.out.print(lit.next() + " ");
}
System.out.println();
ListIterator lit3s = list.listIterator();
//逆序输出每个元素的值
while (lit.hasPrevious()) {
System.out.print(lit.previous() + " ");
}
}
}
程序运行结果:
0 1 2 3 4 5
5 4 3 2 1 0
使用ArrayList添加和删除元素,然后使用ListIterator遍历元素。
import java.util.ArrayList;
import java.util.ListIterator;
public class ListIteratorDemo {
public static void main(String args[]) {
List li = new ArrayList();
for (int i = 1; i < 6; i++) {
li.add(new Integer(i));
}
ListIterator lit = li.listIterator();
//在1和2之间插入6
System.out.println(lit.nextIndex());
lit.next();
lit.add(new Integer(6));
System.out.println(li);
//删除6
lit.previous();
lit.remove();
System.out.println(li);
//将元素3替换成字符'A'
lit.next();
lit.next();
lit.set(new Character('A'));
System.out.println(li);
}
}
程序运行结果:
0
[1, 6, 2, 3, 4, 5]
[1, 2, 3, 4, 5]
[1, 2, A, 4, 5]。
2、增强型for循环for-each
for-each循环是对for循环的增强,主要用来对集合类型对象的遍历,是JDK5.0之后才支持的,但是有一定的约束。
for-each循环的基本结构如下:
for (数据类型 变量名:数组名) {
循环体语句
}
其中,变量的数据类型必须与数组的数据类型一致,变量按照从头到尾的顺序,每次迭代接收数组中的一个元素,循环不断重复,直到获得了数组中的所有元素为止。循环体中的代码主要是对标识变量的操作。如果使用Object表示元素的类型,标识符变量使用obj,操作的数组或者集合为collection,要执行的语句为statements,for-each循环的格式如下:
for(Object obj: collection){
statements;//要执行的语句
}
以上代码等同于如下for循环结构代码:
for(int i=0;i<collection.size();i++){
Object obj = collection.get(i);
statements;//要执行的语句
}
分别使用for循环为数组赋值,并分别使用for循环和for-each循环输出数组元素的值。
public class ForEachTest {
public static void main(String[] args) {
int a[] = new int[5];
//通过for循环为数组元素赋值
for(int i=0;i<5;i++){
a[i] = 2*i;
}
//通过for循环输出数组元素的值
System.out.println("使用for循环输出数组元素的值");
for(int i=0;i<5;i++){
System.out.println(a[i]);
}
//通过for-each循环输出数组元素的值
System.out.println("使用for-each循环输出数组元素的值");
for(int i:a){
System.out.println(i);
}
}
}
程序运行结果:
使用for循环输出数组元素的值
0 2 4 6 8
使用for-each循环输出数组元素的值
0 2 4 6 8
【注意】for-each循环中的循环变量表示集合或者数组中的元素,而不是索引号。
使用for-each循环遍历ArrayList对象。
import java.util.ArrayList;
public class ForEachTest2 {
public static void main(String[] args){
//创建ArrayList对象,并赋值
List<String> users = new ArrayList<String>();//泛型下一节会讲到
for(int i=0;i<5;i++){
users.add("user"+(i+1));
}
//使用for-each循环遍历ArrayList对象
for(String user:users){
System.out.println(user);
}
}
}
程序运行结果:
user1 user2 user3 user4 user5
使用for-each与迭代器循环遍历ArrayList。
import java.util.Vector;
public class EnhanceFor {
public static void main(String args[]){
//遍历集合中的每一个元素
List li = new ArrayList();
for (int i = 0; i < 5; i++) {
li.add(i);
}
for (Object num : li) {
System.out.print(num);
}
System.out.println();
for (Iterator it = li.iterator(); it.hasNext(); ) {
Integer num = (Integer) it.next();
System.out.print(num);
}
//遍历数组中的每个元素
int[] arr = new int[5];
for (int i = 0; i < arr.length; i++) {
arr[i] = i + 5;
}
System.out.println();
for (int n : arr) {
System.out.print(n);
}
System.out.println();
for (int i = 0; i < arr.length; i++) {
int n = arr[i];
System.out.print(n + " ");
}
}
}
程序运行结果:
0 1 2 3 4
0 1 2 3 4
5 6 7 8 9
5 6 7 8 9
我们在遍历集合时通常会使用增强的for循环,其用法如下:
1.首先增强for循环和iterator遍历的效果是一样的,也就说增强for循环的内部也就是调用iterator实现的,但是增强for循环有些缺点,例如不能在增强循环里动态的删除集合内容。不能获取下标等。
2.ArrayList由于使用数组实现,因此下标明确,最好使用普通循环。
3.而对于LinkedList由于获取一个元素,要从头开始向后找,因此建议使用增强for循环,也就是iterator。