目录
注意:
这篇文章适合初学集合的新手,主要讲的是集合的基本用法,依托于Collection。
一、从“痛点切入”,我们为什么需要集合
开篇案例对比:
// 用数组存储3个学生信息,再新增1个学生
Student[] students = new Student[3];
students[0] = new Student("张三", 18);
students[1] = new Student("李四", 19);
students[2] = new Student("王五", 20);
// 想新增第4个学生?必须创建新数组,复制旧数据
Student[] newStudents = new Student[4];
System.arraycopy(students, 0, newStudents, 0, 3);
newStudents[3] = new Student("赵六", 21);
大家看完这段代码,相信已经发现了我们以往所用的引用数据类型的痛点。
------------------数组长度固定,使用不方便--------------
---------存放数据成员类型固定,不灵活-----------------
---------内存空间连续,内存利用率不高-----------------
-----------------插入删除元素效率低------------------------
所以,我们的java提供了一种“容器”来解决这种问题
-----集合
它能自动扩容、提供丰富的操作方法,还能根据需求选择不同的数据结构。
二、集合的核心概念
1.集合框架的“家谱”
这节课,我们主要学的,也就是单列集合:根接口(Collection)这条线。
// 根接口:Collection 子接口:List 实现类:ArrayList
//创建对象时,一般要指定泛型。
//前面指定泛型,后面可以省略
//Collection<String> collection=new ArrayList<String>();
Collection collection=new ArrayList<>();
2.与数组的核心区别
特性 | 数组 | 集合 |
---|---|---|
长度 | 固定(声明时确定) | 动态可变(自动扩容) |
存储类型 | 基本类型 / 引用类型 | 仅能存引用类型(基本类型会自动装箱) |
元素重复性 | 允许重复 | List 允许,Set 不允许 |
操作方法 | 仅 length 属性 + Object 方法 | 丰富(add/remove/contains 等) |
3.集合的基本用法
public class Test {
public static void main(String[] args) {
// 常见关系:
// 根接口:Collection 子接口:List 实现类:ArrayList
//创建对象时,一般要指定泛型。
//前面指定泛型,后面可以省略
//Collection<String> collection=new ArrayList<String>();
Collection collection=new ArrayList<>();
//使用集合
if(collection.isEmpty()){
System.out.println("collenction为空");
}
System.out.println("长度:"+collection.size());
collection.add("hello");
//思考:不是只能存引用数据类型吗?为什么可以放1和a?
collection.add(1);
int a=1;
collection.add(a);
Integer integer=1;
collection.add(integer);
collection.add('a');
int[] arr={1,2,3};
collection.add(arr);
}
}
细心的小伙伴已经发现在与数组的核心区别中,我在存储类型后面写了个自动装箱。
Collection<Integer> coll = new ArrayList<>();
coll.add(10); // 自动装箱:int→Integer(引用类型)
coll.add(20);
System.out.println( coll.size()); // 输出2(长度),体现动态扩容
三、详解:从接口到实现
1.Collection接口通用方法
以生活容器场景类比:
add(E e):往容器里放东西;
remove(Object o):从容器里拿出某个东西;
contains(Object o):检查容器里有没有某个东西;
size():看看容器里有多少东西;
isEmpty():检查容器是不是空的;
addAll(Collection c):把另一个容器里的东西全倒进来
具体实战:
public class Test02 {
public static void main(String[] args) {
Collection<String> collection=new ArrayList<>();
//常用方法测试-----------------------
collection.add("hello");
//boolean contains(Object var1);//判断是否包含
boolean flag1=collection.contains("hello");
System.out.println(flag1);
//----------------------------------------------------
Collection collection2=new ArrayList<>();
//addAll
collection.addAll(collection2);
//boolean containsAll(Collection<?> var2);
boolean flag2=collection.addAll(collection2);
//----------------------------------------------------------
//toArray();
Object[] obj=collection.toArray();
System.out.println(obj);
System.out.println(Arrays.toString(obj));
//remove 移除
collection.remove("!");//------无返回
boolean flag3=collection.remove("1");//-----有返回,只判断,不删除
boolean flag4=collection.removeAll(collection2);
//clear();
collection.clear();
//equals
boolean flag5=collection.equals(collection2);
//hashCode()
//为什么集合的hashCode有的是正数有的是负数?
System.out.println(collection.hashCode());
System.out.println(collection2.hashCode());
}
}
2.自建类Student
那如果,我们创建了一个类(Student),我们的Collection该如何派上用场呢?
public class Student {
private String name;
private int age;
private String sex;
public Student() {
}
public Student(String name, int age, String sex) {
this.name = name;
this.age = age;
this.sex = sex;
}
//set...get....
//toString必须重写
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", sex=" + sex + "]";
}
}
将刚刚的方法利用在Student的测试类上,你会不会使用呢?
练习一:补全代码
尝试用上面所学的Collection中的方法补全代码
public class Test {
public static void main(String[] args) {
Collection<Student> collection=new ArrayList<>();
Student student=new Student("李01",18,"男");
//如:add
//.........
}
}
3.三种遍历
(1)普通for循环://遍历1--for+toArray
Object[] arr=collection.toArray();
for(int i=0;i<arr.length;i++){
System.out.println(arr[i]);
}
(2)迭代器(Iterator):
Iterator<String> iterator= collection.iterator();//获取迭代器对象
while (iterator.hasNext()) {
String string=iterator.next();
System.out.println(string);
}
(3)增强for循环:
for(Object obj:collection.toArray()){
System.out.println(obj);
}
在选择三种遍历方式的时候,要考虑我们的实际需求。
只读-------加强for循环
修改-------普通for循环
删除-------迭代器
四、习题答案
public class Test {
public static void main(String[] args) {
Collection<Student> collection=new ArrayList<>();
Student student=new Student("李霄01",18,"男");
//add
collection.add(student);
Collection<Student> collection2=new ArrayList<>();
Student student2=new Student("李霄02",18,"女");
collection2.add(student2);
System.out.println(collection.size());
//addAll
collection.addAll(collection2);
System.out.println(collection);
System.out.println(collection2);
//contains
boolean flag1=collection.contains("李霄");
boolean flag2=collection.containsAll(collection2);
System.out.println(flag1);
System.out.println(flag2);
//toArray;
Object[] obj1= collection.toArray();
Object[] obj2= collection2.toArray();
//remove
collection.remove("18");
boolean flag3= collection2.remove("18");
System.out.println(collection);
System.out.println(flag3);
System.out.println(collection2);
}
}
五、答疑解惑
1.集合为什么不能存储基本数据类型?
因为集合设计初衷是操作对象(引用类型),基本类型会通过自动装箱(如int→Integer)转为包装类(引用类型)存入。
2.为什么hashCode有时候是正数有时候是负数?
Java 中hashCode()的返回值是int类型,而int的取值范围是 -2³¹ ~ 2³¹-1(即 - 2147483648 ~ 2147483647)。
3.equals和hashCode的关系?
两个对象equals为 true,则hashCode必须相等;
hashCode相等,equals不一定为 true(哈希冲突)。
4.ArrayList扩容机制?
初始容量 10,满了之后扩容为原来的 1.5 倍(旧容量 + 旧容量 >>1),通过数组复制实现。
知其然
更知其所以然