第十八章 Collection和List
1.作业回顾
//写一个TestException类,在main方法中接受两个命令行参数,
//将他们转换成整数,并用第二个数除以第一个数,打印结果。
//测试一下情况,某个参数不是数字(异常1),第二个参数为0.(异常2)
public class Day17HomeWork {
public static void main(String[] args) {
System.out.println(args[0]);//1
System.out.println(args[1]);//2
try {
int a = Integer.parseInt(args[0]);
int b = Integer.parseInt(args[0]);
System.out.println(b / a);//1
}catch(NumberFormatException e1) {
System.out.println("参数必须为整数");
}catch(ArithmeticException e2) {
System.out.println("不能除0");
}
}
}
2.集合体系接口
数组不是动态的,一个数组一旦创建,它的容量就是固定的不能被修改。为了添加新的元素,需要创建一个容量更大的数组,并且将数据拷贝到新的数组中。
数组新增元素示例:
import java.util.Arrays;
public class Day1802 {
public static void main(String[] args) {
int[] arr = new int[5];
arr[0] = 0;
arr[1] = 1;
arr[2] = 2;
arr[3] = 3;
arr[4] = 4;
// arr[5] = 5;//java.lang.ArrayIndexOutOfBoundsException
int[] arr1 = Arrays.copyOf(arr, 6);
System.out.println(arr1.length);//6
}
}
集合是动态的,集合允许动态的添加,删除元素。当元素的数量超过集合的容量时,集合会自动扩容。
java中有一系列的集合,它们有各自的用途。
3.Collection接口
Collection接口是Collection体系中最顶层的接口。一个Collection代表了一组对象,这些对象被称为元素。
Collection示意图:
集合只能保存对象,不能保存基本数据类型,可以使用包装类。Collection接口定义了所有集合必须实现的通用方法。有些类型的集合允许重复的元素,有些则不允许。有些类型的集合是有序的,有些不是。java并没有提供此接口的具体实现类,而是提供了它的子接口如Set和List的具体实现类。
3.1 Collection接口简介
Collection接口API: 这里的E表示集合中的元素的类型
import java.util.Iterator;
public class Day1803 {
interface Collection<E> extends Iterable<E> {
boolean add(E paramE);//添加一个元素
boolean remove(Object paramObject);//删除一个元素
int size();//获取集合中元素的数量
boolean isEmpty();//判断集合是否为空
void clear();//清空集合中的元素
boolean contains(Object paramObject);//判断集合中是否包含一个元素
Iterator<E> iterator();//获取此集合的一个迭代器,通过迭代器可以遍历集合中的元素
//还有很多方法
}
}
3.2 Collection接口的使用
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
public class Day1803 {
public static void main(String[] args) {
//使用泛型参数String创建的集合,只能向其加入String类型的元素
//c是集合类型变量,new HashSet<String>()是集合对象
Collection<String> c = new HashSet<String>();
//判断集合是否为空
System.out.println(c.isEmpty());//true
//获取集合中元素的数量
System.out.println(c.size());//0
c.add("张三");//向集合加入一个元素,元素在集合中是无序存储的
System.out.println(c.isEmpty());//false
System.out.println(c.size());//1
c.add("李四");
c.add("李四");
c.add("李四");
c.add("王五");
c.add("马六");
//contains方法需要元素实现了equals方法,它会将传入的元素和集合中的所有元素依次
//使用equals方法进行比较
System.out.println(c.contains("王五"));//true
//删除指定所有元素,需要元素实现了equals方法
c.remove("李四");
//由于Collection集合不记录元素的索引位置,必须使用迭代器来遍历集合
//获取集合的迭代器
Iterator<String> i = c.iterator();
//hashNext方法判断集合中是否还有下一个元素
//next方法获取集合的下一个元素
while(i.hasNext()) {
System.out.print(i.next());
}//张三马六王五
System.out.println();
//foreach可以应用于实现了Iterable接口的集合
//Collection接口继承于Iterable接口,因此Collection所有的子集合都可以获取迭代器
//foreach内部就是使用迭代器
for (String str : c) {
System.out.print(str);
}
}
}
4.List接口
List接口继承于Collection接口,因此它拥有Collection接口的所有方法。List接口是一个有序列表,它保留元素的添加顺序,并且允许将元素添加到集合中的指定位置。 List接口类似数组,只不过它是可以动态扩展。
List接口示意图:
当添加一个新的元素时,可以使用void add(E paramE)方法将元素添加到List集合的末尾。 也可以使用void add(int paramInt, E paramE)方法将元素添加到指定的位置。这样原来在此位置的元素和后续的元素都会向下移动。 也可以使用set和get方法来修改和获取指定位置的元素。 List接口的方法,未列全。
import java.util.Collection;
import java.util.ListIterator;
interface List<E> extends Collection<E>{
E get(int paramInt);//获取指定位置的元素
E set(int paramInt, E paramE);//替换指定位置的元素,返回旧元素
void add(int paramInt, E paramE);//方法将元素添加到指定的位置
E remove(int paramInt);//删除指定位置的元素
int indexOf(Object paramObject);//获取指定元素的位置
int lastIndexOf(Object paramObject);//获取指定元素的位置,从后向前
ListIterator<E> listIterator();//返回一个迭代器
List<E> subList(int paramInt1, int paramInt2);//返回子列表
}
List接口有两个实现类:ArrayList和LinkedList
4.1 ArrayList
4.1.1 ArrayList的使用
ArrayList实现了List接口,它保存元素的添加顺序。ArrayList的内部实现是一个数组。
import java.util.ArrayList;
import java.util.List;
public class Day1804 {
public static void main(String[] args) {
//创建了一个ArrayList集合,可以保存Integer类型的对象
List<Integer> list = new ArrayList<Integer>();
list.add(1);//自动装箱
list.add(2);//自动装箱
list.add(3);//自动装箱
//List集合保留元素的添加顺序
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.2 ArrayList可以保存重复元素
import java.util.ArrayList;
import java.util.List;
public class Day1805 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
//List允许重复元素
list.add("张三");
list.add("张三");
System.out.println(list.size());//2
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
System.out.println(list.indexOf("张三"));//0
list.add(0, "李四");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.3 set方法用于替换指定位置的元素
import java.util.ArrayList;
import java.util.List;
public class Day1806 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
list.set(1, "马六");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.4 remove有两个重载的方法
import java.util.ArrayList;
import java.util.List;
public class Day1807 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("张三");
list.add("李四");
list.add("王五");
list.remove(2);//删除李四
list.remove("张三");//删除第一个张三,只删一个
System.out.println(list.size());//2
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
import java.util.ArrayList;
import java.util.List;
public class Day1808 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("张三");
list.add("张三");
list.add("张三");
list.add("李四");
//删除所有的张三
for (int i = 0; i < list.size(); i++) {
int index = list.indexOf("张三");
if (index != -1) {
list.remove(index);
i--;
}
}
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.5 List的排序
Collections.sort()方法可以对List进行排序,前提是List中的元素实现了Comparable接口。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Day1809 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));//按添加元素顺序输出
}
System.out.println();
//按照字母顺序排序
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));//按字母顺序输出
}
System.out.println();
List<Integer> list1 = new ArrayList<Integer>();
list1.add(2);
list1.add(1);
list1.add(4);
list1.add(3);
for (int i = 0; i < list1.size(); i++) {
System.out.println(list1.get(i));//按添加元素顺序输出
}
//对集合中的元素进行排序
//按照从小到大的顺序排序
Collections.sort(list1);
System.out.println();
for (int i = 0; i < list1.size(); i++) {
System.out.println(list1.get(i));
}
}
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
class Student implements Comparable<Student> {
private String name;
private int age;
private int sno;
public Student(String name, int age, int sno) {
super();
this.name = name;
this.age = age;
this.sno = sno;
}
@Override
public String toString() {
return "Student [name=" + name + "]";
}
//如果返回负数代表我在前,参数在后
//如果返回正数代表我在后,参数在前
@Override
public int compareTo(Student o) {
//按照年龄从小到大排序
// return this.age - o.age;
//按照学号从大到小排序
return this.sno - o.sno;
}
}
public class Day1810 {
public static void main(String[] args) {
List<Student> list = new ArrayList<Student>();
list.add(new Student("张三", 18, 20191101));
list.add(new Student("李四", 19, 20191102));
list.add(new Student("王五", 20, 20191103));
Collections.sort(list);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.6 使用比较器进行排序
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
public class Day1811 {
public static void main(String[] args) {
List<Person> list = new ArrayList<Person>();
list.add(new Person("张三", 18));
list.add(new Person("李四", 19));
list.add(new Person("王五", 20));
//元素不实现Comparable接口,可以传递一个比较器
Collections.sort(list, new Comparator<Person>() {
public int compare(Person o1, Person o2) {
//按照年龄从小到大排序
return o1.getAge() - o2.getAge();
}
}
);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.1.7 将List转化为数组
import java.util.ArrayList;
import java.util.List;
public class Day1812 {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
String[] arr = new String[list.size()];
//将list转换为数组
list.toArray(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
4.1.8 将数组转换为List
import java.util.Arrays;
import java.util.List;
public class Day1813 {
public static void main(String[] args) {
String[] arr = {"a", "b", "c", "d"};
//将数组转换为list
List<String> list = Arrays.asList(arr);
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.2 LinkedList
LinkedList实现了List接口,LinkedList和ArrayList拥有同样的方法,但是内部的实现却不同。ArrayList内部使用数组来实现,LinkedList内部使用链表来实现。
数组是一块连续的内存单元,链表不是连续的内存单元。
链表通过引用来使链表中的元素关联起来。
LinkedList示意图:
import java.util.LinkedList;
import java.util.List;
public class Day1814 {
public static void main(String[] args) {
List<String> list = new LinkedList<String>();
list.add("one");
list.add("two");
list.add("three");
list.add("four");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
}
}
4.3LinkedList和ArrayList对比
ArrayList增删元素慢,因为需要拷贝整个数组。而LinkedList增删元素快,因为只需要修
改引用。但是,这是理论上的,实际并非如此。但是ArrayList的查询速度明显快于LinkedList。
5.Set接口
Set接口继承于Collection接口,因此它拥有Collection接口的所有方法。它并没有添加新的方法,只是约定不能添加重复的元素。
Set示意图:
Set接口API:
interface Set<E> extends Collection<E>{
}
5.1HashSet
HashSet实现了Set接口,不允许重复元素,依赖于元素重写了equals方法。Set是无序的集合,不保存添加元素的顺序,但是对Set进行迭代,每次的顺序都是一致的。
import java.util.HashSet;
import java.util.Set;
public class Day1815 {
public static void main(String[] args) {
Set<String> set = new HashSet<String>();
set.add("张三");
set.add("李四");
set.add("王五");
set.add("马六");
System.out.println(set.add("张三"));//false, 没有添加成功
//对set进行迭代,每次的顺序都是一致的
for(String str : set) {
System.out.println(str);
}
}
}
import java.util.HashSet;
import java.util.Set;
class Animal {
private String name;
private int age;
public Animal(String name, int age) {
super();
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Animal [name=" + name + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Animal other = (Animal) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
public class Day1816{
public static void main(String[] args) {
Set<Animal> set = new HashSet<Animal>();
set.add(new Animal("小狗", 1));
set.add(new Animal("小猫", 2));
set.add(new Animal("小狗", 1));
for (Animal animal : set) {
//重写了equals方法,则小狗1重复了
System.out.println(animal);
}
}
}
6.作业
1,从键盘随机输入10个整数,保存到List中,并按照倒序排序,从大到小的顺序显示出来。