集合有时又称为容器,简单的说,它是一个对象,能将具有相同性质的多个元素汇聚成一个整体。
集合被用于存储、获取、操纵和传输聚合的数据。
在实际开发中,需要将使用的对象存储于特定的数据结构的容器中。JDK 提供了这样的容器——集合(Collection)和Map
如图所示:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qdvsf4OY-1591371282209)(C:\typora(日志)\JAVA基础复习日志\images\集合框架图.jpg)]
Collection 是一个接口,定义了集合相关的操作方法,其有两个子接口分别是 List 和 Set。Map 接口中有两个实现类分别是 HashMap 和 Hashtable(????)。
在集合框架类中有很多实现类,不用每个都掌握,只需要掌握常用的就行
一个集合代表一组对象,这些对象作为集合的元素。
在 Java 集合框架中定义的 Collection 接口,提供了集合接口的通用操作,通常用于传递集合对象。
Collection 接口是 Set 接口和List 接口的父接口,提供了大量的、通用的集合操纵方法。
Collection 定义了一个 add 方法用于向集合中添加新元素: Boolean add(E e)
该方法会将给定的元素添加进集合中,若添加成功则返回true,否则返回false
具体如下:
public class Collection {
public static void main(String[] args) {
ArrayList coll = new ArrayList();
coll.add(1);
coll.add(1);
coll.add(1);
System.out.println(coll);//[1,1,1]
}
}
Boolean contains(Object o)
该方法用于判断给定的元素是否包含在集合中,若包含则返回true,否则返回false。需要注意的是,集合在判断元素是否被包含在集合中,是根据每个元素的 equals() 方法进行比较后的结果,通常需要重写 equals() 方法,保证 contains 方法的合理结果
实际案例:略
(1) int size():该方法用于返回当前集合中的元素总数
(2) void clear():该方法用于清除当前集合
(3) boolean isEmpty():该方法用于判断当前集合是否不包含任何元素(返回集合是否为空)
具体如下:
import java.util.ArrayList;
import java.util.List;
public class Collection {
public static void main(String[] args) {
//传入字符串型的元素
List<String> list = new ArrayList<String>();
System.out.println(list.isEmpty());//true
list.add("java");
list.add("c");
list.add("#c");
System.out.println("isEmpty:"+list.isEmpty()+"\nsize:"+list.size());
//清除集合元素
list.clear();
System.out.println("isEmpty:"+list.isEmpty()+"\nsize:"+list.size());
}
}
/*运算结果:
true
isEmpty:false
size:3
isEmpty:true
size:0
*/
上述程序中 List 在定义时加了一个 < String> ,该语法是泛型,后续将介绍。作用是要求 list 集合中只能存储 Stirng 类型的元素,如果不加泛型,集合可以存放任意类型的元素。
(1) Boolean addAll(Collection Extends E> c):该方法需要传入一个集合,并将该集合中所有元素添加到当前集合中
(2) Boolean containsAll(Collection> c):该方法用于判断当前集合是否包含给定集合中的所有元素,若包含则返回 true
具体如下:
import java.util.ArrayList;
import java.util.List;
public class CollectionDemo {
public static void main(String[] args) {
List<String> c = new ArrayList<String>();
c.add("Java");
c.add("C");
c.add("PHP");
System.out.println(c);
List<String> c1 = new ArrayList<String>();
//将集合C中的所有元素传入到集合C1中去
c1.addAll(c);
System.out.println(c1);
List<String> c2 = new ArrayList<String>();
c2.add("java");
c2.add("PHP");
System.out.println(c2);
//判断集合C是否包含集合C2中的所有元素
System.out.println(c.containsAll(c2));
}
}/*远算结果:[Java, C, PHP]
[Java, C, PHP]
[java, PHP]
true
*/
Collection中除了上述例句的方法以外,还有其他很多方法,方法如下:
(1) boolean add(object o):该方法用于向集合里添加一个元素
(2) boolean addAll(Collection c):该方法把集合c里的所有元素添加到指定集合里
(3) void clear():清楚集合里的所有元素,将集合长度变为0
(4) boolean contains(Object c):
(5) boolean containsAll(Object c):
(6) boolean isEmpty():返回集合是否为空。当集合长度为0时返回true,否则返回false
(7) Iterator iterator():返回一个Iterator对象,用于遍历集合里的元素
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOpDqDgO-1591371282211)(C:\typora(日志)\JAVA基础复习日志\images\Iterator.png)]
代码如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class CollectionDemo {
public static void main(String[] args) {
List<String> c = new ArrayList<String>();
c.add("Java");
c.add("C");
c.add("PHP");
System.out.println(c);
//遍历集合里的元素
Iterator<String> it = c.iterator();
//hasNext
while(it.hasNext()) {
System.out.println(it.next());
}
}
}/*远算结果:[Java, C, PHP]
Java
C
PHP
*/
(8) boolean remove(Object o):删除集合中的指定元素o,当集合包含了一个或多个元素o时,这些元素将被删除,该方法返回true
(9) boolean removeAll(Object c):从集合中删除集合c里包含的所有元素。。。。。。
(10) boolean retainAll(Collection c):从集合中删除集合c里不包含的元素
(11) int size():返回集合里元素的个数
(12) Object[] toArray():把集合转换成一个数组,所有集合元素变成对应的数组元素
通过上述代码以及运行程序后的结果,可以看出Collection的用法有添加、删除、返回元素个数以及清除元素等
Iterator 接口也是Java集合框架中的成员,但它与 Collection 系列、Map系列的集合不一样。Collection 系列集合、Map系列集合主要用于盛装其他对象
迭代:迭代是重复反馈过程的活动,其目的通常是为了逼近所需目标或结果。每一次对过程的重复称为一次“迭代”,而每一次迭代得到的结果会作为下一次迭代的初始值。重复执行一系列运算步骤,从前面的量依次求出后面的量的过程。
而 Iterator 则主要用于遍历(即 迭代访问 )Collection 集合中的元素,Iterator 对象也被称为迭代器
Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的同一编程接口。
Iterator 接口定义了如下3个方法:
(1) boolean hasNext():如果被迭代的集合元素还没有被遍历,则返回true
(2) Object next():返回集合里下一个元素
(3) void remove():删除集合里上一次next方法返回的元素
具体代码如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("JAVA");
list.add("C");
list.add("PHP");
System.out.println(list);
//获取list集合对应的迭代器
Iterator itera = list.iterator();
while(itera.hasNext()) {
//itera.next() 方法返回的数据类型是Object类型,需要强制转换
String str = (String) itera.next();
System.out.println(str);
/*对迭代变量str进行赋值,但再此输出时集合元素并没有发生改变。
结论:当使用Iterator对集合元素进行迭代时,Iterator并不是把集合元素本身传给迭代变量,
而是把集合元素的值传给迭代变量,所以修改迭代变量的值对元素本身没有任何影响
*/
str="测试字符串";
}
//测试输出行
System.out.println(list);
}
}
/*运算结果:
[JAVA, C, PHP]
JAVA
C
PHP
[JAVA, C, PHP]
*/
从上面代码中可以看出,Iterator 仅用于遍历集合,本身并不提供盛装对象的能力。如果要创建Iterator对象,则必须有一个被迭代的集合。没有集合的Iterator就没有存在的价值。
Iterator 必须依附于 Collection 对象,若有一个 Iterator 对象,则必然有一个与之关联的 Collection 对象。
Iterator 提供了两个方法来迭代访问 Collection 集合里的元素,并可以通过 Remove() 方法来删除集合中上一个 next() 方法返回的集合元素。
在使用迭代器遍历集合时,不能通过集合的 remove() 方法删除集合元素,否则会抛出并发生异常。我们只能通过使用Iterator 迭代器自身的 remove() 方法来删除通过 next() 迭代出来的元素。
使用方式:
迭代器的删除方法是在原集合中删除元素,需要注意的是,在调用 remove() 方法前必须通过迭代器 next() 方法迭代过的元素。这样删除的才是这个元素,而且删除后的不能再次调用 remove( )方法,除非再次调用 next() 方法。
当 Iterator 用作迭代访问 Collection 集合元素时,Collection 集合里的元素不能被改变,只能通过 Iterator的 remove() 方法删除上一次 next() 方法返回的集合元素才可以,否则会发生异常java. util. Concurrent Modification Exception
代码如下:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorRemove {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("JAVA");
list.add("C");
list.add("C#");
list.add("PHP");
Iterator iter = list.iterator();
while(iter.hasNext()) {
//强制转换
String ite = (String) iter.next();
if(ite.equals("JAVA")) {
iter.remove();
}else if(ite.equals("C")) {
iter.remove();
}
}
//将元素 "Pytsom" 插入到 List 集合中的 1 处(相当于数组)
list.add(1, "Pytsom");
System.out.println(list);
//集合转换数组
Object[] arr = list.toArray();
for(int i=0;i<arr.length;i++) {
System.out.println(arr[i]+" ");
}
}
}
/*运算结果:
[C#,Pytsom,PHP]
C#
Pytsom
PHP
*/
Java5.0之后推出一个新的特性,增强for循环,也成为新循环。该循环不同于传统的循环工作,其只用于遍历集合或者数组,语法如下:
for(元素类型 e:集合或数组){
循环体;
}
新循环并非新的语法,而是在编译过程中,编译器会将新循环转换为迭代器模式,所有循环本质上是迭代器
具体代码如下:
import java.util.ArrayList;
import java.util.List;
public class ForeachText {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("JAVA");
list.add("C");
list.add("C#");
list.add("PHP");
for(Object e:list) {
//强制转换
String book = (String) e;
System.out.println(book);
if(book.equals("JAVA")) {
//使用Foreach循环时集合元素不可以发生改变,此处代码会发生异常
list.remove(book);
}
System.out.println(list);
}
}
}
List是有序的 Collection ,使用此接口能够精确地控制每个元素插入的位置。用户能够使用索引(元素在List的位置,类似于数组下标)来访问List中的元素,这类似于数组,且List允许有相同的元素。
除了从 Collection 继承过来的操作外,List接口还包括以下操作:
(1) 按位置访问:根据元素在序列中的位置索引访问元素
(2) 查找:在序列中查找指定的对象,并返回其位置索引
(3) 迭代:扩展了 Iterator 接口,以利用序列的顺序特征
(4) List子集合:在序列上执行任何范围的操作
List 作为 Collection 接口的子接口,当然可以使用 Collection 接口里的全部方法,而且由于 List 是有序集合,因此 List 集合里添加了一些根据索引来操作集合元素的方法。
(1) void add(int index,Object e):将元素 e 插入到 List 集合中的 index 处
(2) boolean addAll(int index,Collection e):将集合 c 所包含的所有元素插入到 List 集合的 index 处
(3) Object get(int index):返回集合 index 索引处的元素
(4) int indexOf(Object o):返回对象 o 在 List 集合中第一次出现的位置索引
(5) int lastIndexOf(Object o):返回对象 o 在 List 集合中最后一次出现的位置索引
(6) Object remove(int index):删除并返回 index 索引处的元素
**(7) ** Object set(int index,Object e):将 index 索引处的元素替换成 e 对象,返回新元素
(8) List subList(int a,int b):返回从索引 a (包含) 到索引 b (不包含)处所有集合组成的子集合
所有的 List 实现类都可以调用这些方法来操作集合元素。与Set集合(不理解)相比,List 增加了根据索引来插入、替换和删除集合元素的方法。
代码如下所示:
import java.util.ArrayList;
import java.util.List;
public class CollectionListDemo {
public static void main(String[] args) {
List books = new ArrayList();
//向集合中添加三个元素
books.add(new String("JAVA"));
books.add(new String("PHP"));
books.add(new String("C##"));
System.out.println(books); //[JAVA, PHP, C##]
//插入元素
books.add(1, new String("Web"));
System.out.println(books); //[JAVA, Web, PHP, C##]
//for循环,get()方法输出集合元素
for(int i=0;i<books.size();i++) {
System.out.println(books.get(i));
}
//删除下标为2的元素
books.remove(2);
System.out.println(books); //[JAVA, Web, C##]
//获得索引位置
System.out.println(books.indexOf(new String("JAVA"))); //0
//替换元素
books.set(0, new String("PHP"));
System.out.println(books); //[PHP, Web, C##]
//截取子集合,相当于Strng类的subString方法。
System.out.println(books.subList(1,3)); //[Web, C##]
}
}
上述代码中,List 集合可以根据位置索引来访问集合中元素的位置,因此 List 增加了一种新的遍历集合元素的方法:使用普通的for循环来遍历集合元素。
实现List接口的常用类有:LinkedList、ArrayList和Vector、Stack
LinkedList的用法和ArrayList用法基本相同,但两者的性能不同。
ArrayList 集合在添加和删除时比 LinkedList 慢,ArrayList 集合在查询时比 LinkedList 集合要快。
ArrayList 实现了可变大小的数组。它允许所有元素,包括 Null 。ArrayList 没有同步。
下面为一个ArrayList的实例代码:
import java.util.ArrayList;
public class ArrayListDome {
public static void main(String[] args) {
ArrayList<SList> arr = new ArrayList<SList>() {
{
add(new SList("zhangsan",19));
add(new SList("lisi",19));
}
};
System.out.println("集合的长度为:"+arr.size());
arr.remove(0);
System.out.println(arr);
}
}
class SList{
String name;
int age;
public SList(String name,int age) {
this.name=name;
this.age=age;
}
}
/*运算结果:
集合的长度为:2
[day01.SList@28a418fc]
*/
LinkedList 类是 List 接口的实现类,这意味着它是一个 List 集合,可以利用索引来随机访问集合中的元素。
除此之外,LinkedList 还实现了 Deque 接口,因此它可以被当成双端队列来使用,自然也可以被当成“栈”来使用。
LinkedList集合的用法:
import java.util.LinkedList;
public class LinkedListDome {
public static void main(String[] args) {
LinkedList link = new LinkedList();
//将字符串元素加入栈的顶部
link.offer("JAVA");
//将一个字符串元素加入栈的顶部
link.push("PHP");
//将字符串元素添加到队列的头部(相当于栈的顶部)
link.offerFirst("C##");
for(int i=0;i<link.size();i++) {
System.out.println(link.get(i));
}//C## PHP JAVA
//访问但不删除栈顶的元素
System.out.println(link.peekFirst());
//访问但不删除队列的最后一个元素
System.out.println(link.peekLast());
//将栈顶的元素 弹出"栈"
System.out.println(link.pop());//[PHP, JAVA]
System.out.println(link);
//访问并删除队列的最后一个元素
System.out.println(link.pollLast());
System.out.println(link);//[PHP]
}
}
LinkedList 与 ArrayList 的实现机制完全不同,ArrayList 内部以数组的形式来保存集合中的元素,因此随机访问集合元素时有较好的性能
而 LinkedList 内部以链表的形式来保存集合中的元素,因此随机访问集合元素时性能比较差,但在差入、删除元素时性能比较好
首先,我们先看下列代码:
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
List links = new ArrayList();
links.add("PHP");
links.add("C##");
//报错
links.add(100);
for(int i=0;i<links.size();i++) {
//改为 Object 类型可以输出
String Str = (String) links.get(i);
System.out.println(Str);
}
}
}
/*运算结果:
PHP
C##
报错:java.lang.ClassCastException
*/
在上述的编码过程中,我们发现主要的两个问题:
(1) 当我们将一个对象放入集合中,集合不会记住此类型对象的类型,当再次从集合中取出对象时,该对象的编译类型变成了 Object 类型,但其运行时类型任然为其本身类型。
(2) 但取出集合元素时需要人为的强制类型转换到具体的目标类型,且很容易出现“java.lang.ClassCastException”异常
那么有没有什么办法可以使集合能够记住集合内元素各类型,且能达到只要编译时不出问题,运行时就不会出现“java.lang.ClassCastException”异常呢?答案就是:使用泛型
泛型,即“参数化类型”。
一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。
那么,参数化类型怎么理解呢?顾名思义,就是将类型有原来的具体的类型参数化,类似于方法中的变量参数,此时类型也定义成参数形式(可以称之为类型形参),然后使用/调用时传入的具体类型(类型实参)。看着有些复杂,首先看下面的例子采用泛型的写法。
具体如下:
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
List links = new ArrayList();
links.add("PHP");
links.add("C##");
links.add(100);
// 泛型
List<String> lists = new ArrayList<String>();
lists.add("PHP");
lists.add("C##");
//lists.add(100);报错,只能传入字符串类型的
for(int i=0;i<lists.size();i++) {
//不需要强制转换,不报错
String lis = lists.get(i);
System.out.println(lis);
}
}
}
式(可以称之为类型形参),然后使用/调用时传入的具体类型(类型实参)。看着有些复杂,首先看下面的例子采用泛型的写法。
具体如下:
import java.util.ArrayList;
import java.util.List;
public class GenericTest {
public static void main(String[] args) {
List links = new ArrayList();
links.add("PHP");
links.add("C##");
links.add(100);
// 泛型
List<String> lists = new ArrayList<String>();
lists.add("PHP");
lists.add("C##");
//lists.add(100);报错,只能传入字符串类型的
for(int i=0;i<lists.size();i++) {
//不需要强制转换,不报错
String lis = lists.get(i);
System.out.println(lis);
}
}
}