进入Java集合的学习,集合的数学概念是指具有某种特定性质的具体的或抽象的对象汇总而成的集体。在Java中的集合也是类似的,先学习集合的框架,这次主要介绍一下Conllection接口。
数组存储具有两大缺点: 1)初始化后长度不可变,难以保存数量变化的数据。 2)只能按索引顺序存取,无法保存具有映射关系的数据。 如成绩表为语文——79,数学——80,这种数据看上去像两个数组,但这两个数组的元素之间有一定的关联关系。
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组)
集合类主要负责保存、盛装其他数据,因此也被称为容器类。Java 所有的集合类都位于 java.util 包下,提供了一个表示和操作对象集合的统一构架,包含大量集合接口,以及这些接口的实现类和操作它们的算法。
1)接口和实现类相分离。 2)支持泛型,可以限制在一个集合中只能放入同一种数据类型的元素,如
List list = new ArrayList<>(); // 只能放入String类型。关于泛型后续会再讲。
3)通过统一的方式——迭代器(Iterator) 实现访问(遍历)集合,而无需知道集合内部元素是按什么方式存储的。
在存储的元素类型上
数组元素既可以是基本类型的值,也可以是对象(实际上保存的是对象的引用变量)。
集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。
集合操作的一般步骤如下
创建集合对象(容器)---->创建元素对象(物品)---->将元素添加进集合(物品装入容器)---->集合操作(相关方法如add()、remove()…/遍历集合/…)
遍历集合的三种方式---->iterator迭代器、for-each循环、for循环
Collection 接口:Iterable 的子接口,也是 List、Set 和 Queue 的父接口(存放一组单值的最大接口,单值:集合中的每个元素都是一个对象),一般很少直接使用此接口直接操作。
Iterator 接口 :集合的输出接口,主要用于遍历输出Collection 集合中的元素,Iterator 对象被称之为迭代器。迭代器接口是集合接口的父接口,实现类实现 Collection 时就必须实现 Iterator 接口。
Collection 接口中常用的方法可以看成是对容器的操作
add(Object o)、addAll(Collection c) (前者为元素增删操作,后者为集合整体元素增删操作,以下同理)
remove(Object o)、removeAll(Collecrion c)
contains(Object o)、containsAll(Collection c),判断是否包含
其他如 clear()、isEmpty()、size()、toArray() (把集合转换为一个数组)等等。不必硬记,会用就行,更多参考java API文档。
Iterator迭代器(推荐)、for-each循环、for循环
1)Iterator迭代器和for-each循环迭代变量不是集合元素本身,系统只是依次把集合元素的值赋给迭代变量,所以在遍历时不能对 Collection 集合里的元素进行修改,否则会抛出 ConcurrentModificationException 2)for循环在遍历时可以对 Collection 集合里的元素进行修改。 3)java.util 包下的集合类都是快速失败的,不能在多线程下发生并发修改。
直接看代码,示例1
import java.util.ArrayList; //导包
import java.util.Iterator;
public class CollectionDemo {
public static void main(String[] args) {
ArrayList list1 = new ArrayList(); // 创建集合 list1
ArrayList list2 = new ArrayList(); // 创建集合 list2
list1.add("one"); // 向 list1 添加一个元素
list1.add("two"); // 向 list1 添加一个元素
list2.addAll(list1); // 将 list1 的所有元素添加到 list2
list2.add("three"); // 向 list2 添加一个元素
System.out.println("list2 集合中的元素如下:");
Iterator it1 = list2.iterator(); //获取迭代器对象
while (it1.hasNext()) { //遍历集合
System.out.print(it1.next() + "、");
}
}
}
运行结果
list2 集合中的元素如下:
one、two、three、
示例2
import java.util.ArrayList; //导包
import java.util.Iterator;
public class CollectionDemo {
public static void main(String[] args) {
ArrayList list1 = new ArrayList(); // 创建集合 list1
ArrayList list2 = new ArrayList(); // 创建集合 list2
list1.add("one");
list1.add("two");
list1.add("three");
System.out.println("list1 集合中的元素数量:" + list1.size()); // 输出list1中的元素数量
list2.add("two");
list2.add("four");
list2.add("six");
System.out.println("list2 集合中的元素数量:" + list2.size()); // 输出list2中的元素数量
list2.remove(2); // 删除第 3 个元素
System.out.println("\nremoveAll() 方法之后 list2 集合中的元素数量:" + list2.size());
System.out.println("list2 集合中的元素如下:");
Iterator it1 = list2.iterator();
while (it1.hasNext()) {
System.out.print(it1.next() + "、");
}
list1.removeAll(list2); //删除list1中与list2的所有相同的元素
System.out.println("\nremoveAll() 方法之后 list1 集合中的元素数量:" + list1.size());
System.out.println("list1 集合中的元素如下:");
Iterator it2 = list1.iterator();
while (it2.hasNext()) {
System.out.print(it2.next() + "、");
}
}
}
运行结果
list1 集合中的元素数量:3
list2 集合中的元素数量:3
removeAll() 方法之后 list2 集合中的元素数量:2
list2 集合中的元素如下:
two、four、
removeAll() 方法之后 list1 集合中的元素数量:2
list1 集合中的元素如下:
one、three、
注意:
retainAll() 方法的作用与 removeAll() 方法相反,即保留两个集合中相同的元素,其他全部删除。
Collection 是接口,不能实例化,可以通过其实现类ArrayList调用 Collection 方法
Queue 是 Java 提供的队列实现,有点类似于 List。Dueue 是 Queue 的一个子接口,为双向队列。
List 实现了 Collection 接口,主要有两个常用的实现类:ArrayList 类和 LinkedList 类。
上述方法无需硬记,忘记时导包选中List后ctrl+B查看源码即可
对顺序访问进行了优化。
LinkedList 类采用链表结构保存对象,便于向集合中插入或删除元素。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
随机访问的速度相对较慢。
LinkedList 类随机访问元素的速度相对较慢(随机访问是指检索集合中特定索引位置的元素)。
除了Collection接口和List 接口中的所有方法,还有 addFirst()、addLast()、getFirst()、getLast()、removeFirst() 和 removeLast() 等方法,能把它当成栈(Stack)或队列(Queue)来用。
使用 LinkedList 实现仓库管理系统中商品名称的记录入库,并输出第一个录入的商品名称和最后一个商品名称
import java.util.Iterator;
import java.util.LinkedList;
public class ProductSystem {
public static void main(String[] args){
LinkedList<String> products=new LinkedList<>(); //创建集合对象
String p1 = new String("六角螺母"); //实例化对象
String p2 = new String("10A 电缆线");
String p3 = new String("5M 卷尺");
String p4 = new String("4CM 原木方板");
products.add(p1); //添加对象元素
products.add(p2); //将p2对象添加到LinkedList集合中
products.add(p3);
products.add(p4);
String p5=new String("标准文件夹小柜");
products.addLast(p5); //向集合的末尾添加p5对象
System.out.print("*************** 商品信息 ***************");
System.out.println("\n目前商品有:");
Iterator<String> it=products.iterator(); //获取迭代器,while循环遍历集合(it.next()方法)
while(it.hasNext()){
System.out.print(it.next()+"\t");
}
System.out.println("\n第一个商品的名称为:"+products.getFirst()); //输出第一个商品名称
System.out.println("最后一个商品的名称为:"+products.getLast()); //输出最后一个商品名称
products.removeLast(); //删除最后一个元素
System.out.println("删除最后的元素,目前商品有:");
for(int i=0;i<products.size();i++){ //for循环遍历集合,通过get()方法得到对应索引的元素
System.out.print(products.get(i)+"\t");
}
}
}
运行结果
*************** 商品信息 ***************
目前商品有:
六角螺母 10A 电缆线 5M 卷尺 4CM 原木方板 标准文件夹小柜
第一个商品的名称为:六角螺母
最后一个商品的名称为:标准文件夹小柜
删除最后的元素,目前商品有:
六角螺母 10A 电缆线 5M 卷尺 4CM 原木方板
示例总结(自己完成) 在学习过程中要养成总结的习惯(可以回顾以前所学,加深印象的同时能发现盲区,横扫问题解决不足)。针对上述示例,用到的方法有
1.对象创建:字符串对象的创建、LinkedList集合对象的创建 2.接口方法:Collection接口的方法(add…)、List接口的方法(for循环遍历用到的get()方法得到对应索引的元素…)、LinkedList集合方法(addLast()、removeLast()、getFirst()、getLast()…) 3.遍历方法:三种遍历方法(迭代器、for循环、for-each) 4.迭代器:迭代器对象的创建、迭代器的next()和hasNext()方法 5.泛型…
总结一下ArrayList 类和 LinkedList 类的区别,非常重要,面试可能会问到。两者都是List接口的实现类,区别如下:
1)ArrayList 是基于动态数组数据结构的实现,访问元素速度优于 LinkedList。LinkedList 是基于链表数据结构的实现,占用的内存空间比较大,但在批量插入或删除数据时优于 ArrayList。
2)对于快速访问对象的需求,使用 ArrayList 实现执行效率上会比较好。需要频繁向集合中插入和删除元素时,使用 LinkedList 类比 ArrayList 类效果高。
一个用数组实现的 List(能进行快速的随机访问,效率高且实现了可变大小的数组)
包括Collection接口的所有方法和List接口的方法。
常用:添加—add(),访问—get(),修改—set(),删除—remove,大小—size(),位置—index(),迭代方法(for,for-each,iterator),其他方法可自行总结添加
List subList(int fromlndex, int tolndex)方法:左闭右开。
1)创建一个商品类 Product,在该类中定义 3 个属性和 toString() 方法,分别实现 setter/getter 方法
public class Product { //创建Product类 (标准javabean)
private int id; //封装成员变量
private String name;
private float price;
public Product(){ //无参构造方法
}
public Product(int id,String name,float price){ //带全部参数的构造方法
this.id=id; //this指向当前变量
this.name=name;
this.price=price;
}
public String toString(){ //toString方法返回该对象的字符串
return "商品编号:"+id+" "+"商品名称:"+name+" "+"商品价格:"+price;
}
public int getId() { //提供get和set方法
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getPrice() {
return price;
}
public void setPrice(float price) {
this.price = price;
}
}
2)创建测试类(两种方法比较) 2.1)以前的方法(未用集合)
public class ProductTest { //创建测试类
public static void main(String[] args){
Product pd1 = new Product(4, "木糖醇", 10); //实例化对象
Product pd2 = new Product(5, "洗发水", 12);
Product pd3 = new Product(3, "热水壶", 49);
System.out.println("*************** 商品信息 ***************");
System.out.println(pd1.toString()); //调用toString()方法返回对象字符串
System.out.println(pd2.toString()); //默认不加toString()也行,养成习惯还是要加上。
System.out.println(pd3.toString());
}
}
运行结果
*************** 商品信息 ***************
商品编号:4 商品名称:木糖醇 商品价格:10.0
商品编号:5 商品名称:洗发水 商品价格:12.0
商品编号:3 商品名称:热水壶 商品价格:49.0
2.2)集合方法
import java.util.ArrayList; // 导包
import java.util.List;
public class ProductTest { //创建测试类
public static void main(String[] args){
Product pd1 = new Product(4, "木糖醇", 10); //实例化对象
Product pd2 = new Product(5, "洗发水", 12);
Product pd3 = new Product(3, "热水壶", 49);
List list=new ArrayList(); //创建集合
list.add(pd1); //添加元素(集合中存储的是对象)
list.add(pd2);
list.add(pd3);
System.out.println("*************** 商品信息 ***************");
for (int i=0;i<list.size();i++){ //循环遍历集合,输出集合元素
Product product=(Product) list.get(i); //通过List的get方法(索引)获取值
//向下转型,将获取的Object类强转为Product类(大转小,要强转),Object类是所有类的父类
System.out.println(product);
}
}
}
// 运行结果同上
2 示例 1)subList()方法(要求集合元素全为字符串,即使用泛型)
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SubListDemo { //创建集合类
public static void main(String[] args){
ArrayList<String> arr=new ArrayList<>(); //创建集合,使用泛型(类型为String)
arr.add("one"); //添加元素
arr.add("two");
arr.add("three");
arr.add("four");
arr.add("five");
arr.add("six");
arr.add("seven");
System.out.println("arr集合中的元素数量:"+arr.size());
System.out.println("arr集合中的元素为:");
// for (String s : arr) { for-each循环遍历
// System.out.println(s);
// }
Iterator<String> it=arr.iterator(); //获取迭代器,while循环遍历集合
while (it.hasNext()) {
System.out.print(it.next()+"、"); //设置输出形式
}
List<String> sub_arr=new ArrayList(); //向上转型(小转大,自转,不用强转)
sub_arr=arr.subList(2,5); //从arr集合中截取索引2~5(2,3,4)的元素,保存到sub_arr集合中
System.out.println("\nsub_arr集合中元素数量为:"+sub_arr.size());
System.out.println("sub_arr集合中的元素为:");
it=sub_arr.iterator(); //获取迭代器,while循环遍历集合
while (it.hasNext()){
System.out.print(it.next()+"、");
}
}
}
运行结果
arr集合中的元素数量:7
arr集合中的元素为:
one、two、three、four、five、six、seven、
sub_arr集合中元素数量为:3
sub_arr集合中的元素为:
three、four、five、
实现Collection接口的所有方法。 TreeSet类常用方法(以下 E为数据类型) 1)E first()、E last():返回集合的第一个和最后一个元素 2)E poolFirst()、E poolLast():获取并移除集合的第一个和最后一个元素 3)截取子TreeSet的方法:
SortedSet<E> subSet(E fromElement,E toElement)、SortedSet<E> headSet<E toElement〉、SortedSet<E> tailSet(E fromElement):返回一个新的集合,新集合包含原集合中( fromElement 对象与 toElement对象之间、 toElement 对象之前、fromElement 对象之后)的所有对象。且左闭右开。
需求:有 5 名学生参加考试,当老师录入每名学生的成绩后,程序将按照从低到高的排列顺序显示学生成绩。此外,老师可以查询本次考试是否有满分的学生存在,不及格的成绩有哪些,90 分以上成绩的学生有几名。使用 TreeSet 类创建 Set 集合,完成学生成绩查询功能。
import java.util.Iterator; //导包
import java.util.Scanner;
import java.util.SortedSet;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args){
TreeSet<Double> scores=new TreeSet<>(); //创建TreeSet集合
Scanner sc=new Scanner(System.in);
System.out.println("------------学生成绩管理系统-------------");
for (int i=0;i<5;i++){
System.out.println("第" + (i + 1) + "个学生成绩:");
double score=sc.nextDouble();
scores.add(score); // 将学生成绩转换为Double类型,添加到TreeSet集合中
}
Iterator<Double> it = scores.iterator(); // 创建 Iterator 对象
System.out.println("学生成绩从低到高的排序为:");
while (it.hasNext()) {
System.out.print(it.next() + "\t");
}
System.out.println("\n请输入要查询的成绩:");
double searchScore=sc.nextDouble();
if(scores.contains(searchScore)){ //contains()方法
System.out.println("成绩为: " + searchScore + " 的学生存在!");
}else {
System.out.println("成绩为: " + searchScore + " 的学生不存在!");
}
// 查询不及格的学生成绩,利用截取子TreeSet的方法取两头或中间
SortedSet<Double> score1 = scores.headSet(60.0);
System.out.println("\n不及格的成绩有:");
for (int i = 0; i < score1.toArray().length; i++) { //toArray()方法转化为数组
System.out.print(score1.toArray()[i] + "\t");
}
// 查询90分以上的学生成绩,利用截取子TreeSet的方法取两头或中间
SortedSet<Double> score2 = scores.tailSet(90.0);
System.out.println("\n90 分以上的成绩有:");
for (int i = 0; i < score2.toArray().length; i++) {
System.out.print(score2.toArray()[i] + "\t");
}
}
}
运行结果
------------学生成绩管理系统-------------
第1个学生成绩:
53
第2个学生成绩:
48
第3个学生成绩:
85
第4个学生成绩:
98
第5个学生成绩:
68
学生成绩从低到高的排序为:
48.0 53.0 68.0 85.0 98.0
请输入要查询的成绩:
90
成绩为: 90.0 的学生不存在!
不及格的成绩有:
48.0 53.0
90 分以上的成绩有:
98.0
注:在使用自然排序时只能向 TreeSet 集合中添加相同数据类型的对象,否则会抛出 ClassCastException 异常。如果向 TreeSet 集合中添加了一个 Double 类型的对象,则后面只能添加 Double 对象,不能再添加其他类型的对象,例如 String 对象等。
1)HashSet是 Set 接口的典型实现(Set 集合中最常用的实现类)。按照 Hash 算法存储集合中的元素,具有很好的存取和查找性能。 2)基于 HashMap 实现,为优化査询速度而设计的 Set。HashSet 底层使用 HashMap 来保存所有元素,实现比较简单。
元素相等:两个对象的 hashCode 值相等且通过 equals() 方法比较返回结果为 true,则 HashSet 集合认为两个元素相等。
1)HashSet():构造一个新的空的 Set 集合。
HashSet hs = new HashSet(); // 调用无参的构造函数创建HashSet对象
2)HashSet(Collection<? extends E>c):构造一个包含指定 Collection 集合元素的新 Set 集合。其中,“< >”中的 extends 表示 HashSet 的父类,即指明该 Set 集合中存放的集合元素类型(泛型)。c 表示其中的元素将被存放在此 Set 集合中。
HashSet<String> hss = new HashSet<String>(); // 创建泛型的 HashSet 集合对象
使用 HashSet 创建一个 Set 集合,并向该集合中添加 4 套教程。
import java.util.HashSet; //导包
import java.util.Iterator;
public class HashSetTest {
public static void main(String[] args){
HashSet<String> courseSet=new HashSet<>(); //使用泛型,创建一个空的set集合对象
String course1=new String("Java入门教程"); //创建对象元素
String course2=new String("Python基础教程");
String course3=new String("C语言学习教程");
String course4=new String("Golang入门教程");
courseSet.add(course1); //将元素添加进集合
courseSet.add(course2);
courseSet.add(course3);
courseSet.add(course4);
System.out.println("******书栈网教程******");
Iterator<String> it=courseSet.iterator(); //获取迭代器对象,while循环遍历集合
while(it.hasNext()){
System.out.println("《"+(String) it.next()+"》");
//注意拼接前要先强转为String类型(Object-->String),大转小,要强转
}
System.out.println("有"+courseSet.size()+"套精彩教程!"); //获取集合元素个数
}
}
运行结果
******书栈网教程******
《Java入门教程》
《C语言学习教程》
《Python基础教程》
《Golang入门教程》
有4套精彩教程!
注:如果向 Set 集合中添加两个相同的元素,则后添加的会覆盖前面添加的元素,即在 Set 集合中不会出现相同的元素。