一、对象数组
(一)概述
数组元素可以是基本数据类型,也可以是类对象,这样的数组称为对象数组。在这种情况下,数组的每一个元素都是一个对象的引用。
(二)使用
案例演示
需求:我有3个学生,请把这3个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。
//定义学生类
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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;
}
}
//测试类
public class MyTest {
public static void main(String[] args) {
Student s1 = new Student("小明", 30);
Student s2 = new Student("小红", 20);
//定义学生数组
Student[] students = {s1, s2, new Student("小刚", 34)};
//遍历输出
for (int i = 0; i < students.length; i++) {
Student student = students[i];
System.out.println(student.getName() + "===" + student.getAge());
}
}
}
数组作为一种容器,对容器中的元素的操作不是很方便,所以Java为了我们更方便的去操作容器中的元素,给我们提供了另一种容器,叫做集合
(三)集合和数组的区别
- 数组的长度是固定的,集合的长度是可变的
- 数组可以存储基本数据类型,也可以存储引用数据类型,集合只能存储引用数据类型
- 数组只能存储同一种数据类型,集合可以存储多种数据类型
二、集合
(一)集合的由来
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。
(二)集合继承体系图
(三)Collection集合
1.概述
Collection 是最基本的集合接口,一个 Collection 代表一组 Object,即 Collection 的元素, Java不提供直接继承自Collection的类,只提供继承于的子接口(如List和set)。
2.添加功能
boolean add(Object obj)
:添加一个元素
boolean addAll(Collection c)
:添加一个集合的元素 (给一个集合添加进另一个集合中的所有元素)
案例演示
import java.util.ArrayList;
import java.util.Collection;
public class MyTest18{
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add("abcd");
collection.add(100);//这里的100不是int类型,而是自动装箱后的Integer类型,因为集合只能存储引用数据类型
collection.add(300);
System.out.println(collection);
Collection collection2 = new ArrayList();
collection2.add("qwer");
collection2.add(200);
collection.addAll(collection2);
System.out.println(collection);
System.out.println(collection2);
}
}
3.删除功能
void clear()
:移除所有元素
boolean remove(Object o)
:移除一个元素(如果集合中存在多个该元素,只会移除第一次出现的该元素)
boolean removeAll(Collection c)
:移除一个集合的元素(移除一个以上返回的就是true) 移除的元素是两个集合的交集元素 ,如果没有交集元素 则删除失败 返回false
案例演示1
import java.util.ArrayList;
import java.util.Collection;
public class MyTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
collection.add(10009);
collection.add(103);
collection.add(100);
collection.add(104);
collection.add(100);
collection.add(104);
collection.add(100);
collection.add(1022);
collection.add(100);
collection.add(103);
collection.remove(10009);
//collection.clear();//清空集合中的所有元素
System.out.println(collection);
}
}
案例演示2
import java.util.ArrayList;
import java.util.Collection;
public class MyTest {
public static void main(String[] args) {
Collection collection = new ArrayList();
//往容器中添加元素
collection.add("hello");
collection.add("world");
collection.add("good");
collection.add("morning");
Collection collection2 = new ArrayList();
//往容器中添加元素
collection2.add("hello");
collection2.add("world");
collection2.add("good");
//boolean removeAll (Collection c):移除一个集合的元素(移除一个以上返回的就是true)
// 删除的元素是两个集合的交集元素,如果没有交集元素 则删除失败 返回false
// a.removeAll(b) 会删掉a集合中两个集合的交集元素,b集合不变,如果移除之后,a集合中发生变化返回true否则返回false
boolean b = collection.removeAll(collection2);
System.out.println(b);
System.out.println(collection);
System.out.println(collection2);
}
}
4.检索功能
使用方法同String类:
int indexOf(Object o)
:返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。
int lastIndexOf(Object o)
:返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。
5.判断功能
boolean contains(Object o)
:判断集合中是否包含指定的元素
boolean containsAll(Collection c)
:判断集合中是否包含指定的集合元素(这个集合 包含 另一个集合中所有的元素才算包含 才返回true)
比如:
[1,2,3] containsAll [1,2]=true
[1,2,3] containsAll [2,3,4]=false
boolean isEmpty()
:判断集合是否为空
案例演示
import java.util.ArrayList;
import java.util.Collection;
public class MyTest18 {
public static void main(String[] args) {
//多态
Collection collection = new ArrayList();
//往容器中添加元素
collection.add("hello");
collection.add("world");
collection.add("good");
Collection collection2 = new ArrayList();
collection2.add("world");
collection2.add("good");
//判断集合中是否存在该元素
boolean b1 = collection.contains("good");
System.out.println(b1);
boolean b2 = collection.contains("morning");
System.out.println(b2);
//判断集合是否包含另一个集合的所有元素
boolean b3 = collection.containsAll(collection2);
System.out.println(b3);
//清空集合
collection.clear();
//判断集合是否为空
boolean b4 = collection.isEmpty();
System.out.println(b4);
}
}
6.遍历功能
Iterator<E> iterator()
:获取一个迭代器(主要用于遍历集合)
E next ()
:让迭代器的指针下移并返回迭代的下一个元素(E代表集合元素的具体类型)boolean hasNext ()
:判断迭代器的指针下面是否有下一个元素
案例演示
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class MyTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
//往容器中添加元素
collection1.add("hello");
collection1.add("world");
collection1.add("good");
collection1.add("morning");
//获取一个迭代器 Iterator是对Collection进行迭代的迭代器。
Iterator iterator = collection1.iterator();
System.out.println(iterator);//java.util.ArrayList$Itr@4554617c。Itr是ArrayList的内部类
//boolean hasNext ()
//如果仍有元素可以迭代,则返回 true。
//E next ()
//返回迭代的下一个元素。
//遍历集合
while (iterator.hasNext()){
Object obj = iterator.next();//让指针下移
System.out.println(obj);
}
}
}
为什么迭代器不定义成一个类 , 而是定义成一个接口?
假设迭代器定义的是一个类,这样我们就可以创建该类的对象,调用该类的方法实现集合的遍历。但是呢,Java中提供了很多集合类,而这些集合类的数据结构是不同的,所以,存储的方式和遍历的方式应该是不同的。进而它们的遍历方式也应该不是一样的,所以,就没有定义迭代器类。而是定义成了接口,不提供具体实现。
7.长度功能
int size()
:元素的个数
注意:
- 数组求长度用length属性
- 字符串求长度用length()方法
- 集合求长度用size()方法
案例演示
import java.util.ArrayList;
import java.util.Collection;
public class MyTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
//往容器中添加元素
collection1.add("hello");
collection1.add("world");
collection1.add("good");
collection1.add("morning");
int size = collection1.size();//判断集合长度
System.out.println(size);
}
}
8.交集功能
boolean retainAll(Collection c)
:获取两个集合的交集元素(交集:两个集合都有的元素)
例如:A集合对B集合取交集,获取到的交集元素放在A集合中。返回的布尔值表示的是A集合是否发生变化。A集合发生过变化返回true ,没发生过变化返回false。
案例演示
import java.util.ArrayList;
import java.util.Collection;
public class MyTest {
public static void main(String[] args) {
Collection collection1 = new ArrayList();
collection1.add(10);
collection1.add(20);
collection1.add(30);
collection1.add(40);
collection1.add(50);
collection1.add(60);
Collection collection2 = new ArrayList();
collection2.add(10);
collection2.add(20);
collection2.add(30);
collection2.add(40);
collection2.add(60);
boolean b = collection1.retainAll(collection2);//第一个集合放的是交集元素
System.out.println(b);
System.out.println(collection1);
System.out.println(collection2);
}
}
9.把集合转换为数组
Object[] toArray()
案例演示
需求:将集合转换为数组
方式1
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;
public class MyTest {
public static void main(String[] args) {
//需求:将集合转换为数组
Collection collection1 = new ArrayList();
collection1.add(10);
collection1.add(20);
collection1.add(30);
collection1.add(40);
collection1.add(50);
collection1.add(60);
Integer[] integer=new Integer[collection1.size()];
Iterator iterator = collection1.iterator();
int index=0;
while(iterator.hasNext()){
Object obj = iterator.next();
integer[index++]=(Integer)obj;
}
System.out.println(Arrays.toString(integer));
}
}
方式2
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
public class MyTest {
public static void main(String[] args) {
//需求:将集合转换为数组
Collection collection1 = new ArrayList();
collection1.add(10);
collection1.add(20);
collection1.add(30);
collection1.add(40);
collection1.add(50);
collection1.add(60);
Object[] obj = collection1.toArray();
System.out.println(Arrays.toString(obj));
}
}
(四)List集合
1.概述
List接口是一个有序(存取有序)的 Collection,使用此接口能够精确的控制每个元素插入的位置,能够通过索引(元素在List中位置,类似于数组的下标)来访问List中的元素,第一个元素的索引为 0,而且允许有相同的元素。
2.特点
元素有序(存取有序),并且每一个元素都存在一个索引,元素可以重复。
3.特有功能
void add(int index,E element)
: 在指定索引处添加元素
E remove(int index)
:移除指定索引处的元素 返回的是移除的元素
E get(int index)
:获取指定索引处的元素
E set(int index,E element)
:更改指定索引处的元素 返回的是被替换的元素
List<E> subList ( int fromIndex, int toIndex)
:根据起始索引和结束索引从集合中截取一段元素,放到一个新集合中
案例演示1
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list=new ArrayList();
list.add(100);
list.add(200);
list.add(300);
list.add(400);
list.add(0,10); //可以在指定索引处插入元素
System.out.println(list);
Object set = list.set(0, 20);//将0索引出的元素替换成20,返回的是被替换掉的元素
System.out.println(set);
System.out.println(list);
list.remove(0);//删掉指定索引处的元素
//list.remove(100);不能直接通过这种方式删除元素,会被认为是删除索引100处的元素
list.remove(Integer.valueOf(100));//根据对象删除这个元素
System.out.println(list);
Object o = list.get(0);//获取集合中指定索引处的元素
System.out.println(o);
System.out.println("===============");
//遍历方式1
Iterator iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
System.out.println("===============");
//遍历方式2
for (int i = 0; i < list.size(); i++) {
Object o1 = list.get(i);
System.out.println(o1);
}
}
}
案例演示2
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(200);
list.add(100);
list.add(400);
list.add(500);
list.add(100);
list.add(300);
list.add(100);
list.add(100);
list.add(200);
list.add(100);
list.add(400);
list.add(500);
list.add(100);
list.add(300);
list.add(100);
list.add(500);
list.add(200);
//根据起始索引和结束索引从集合中截取一段元素,放到一个新集合中
List list1 = list.subList(0, list.indexOf(200)+1);//含头不含尾
System.out.println(list1);
System.out.println(list);
}
}
4.特有迭代器
List的迭代器ListIterator 继承自Iterator 可以使用Iterator中的方法
ListIterator的特有功能
boolean hasPrevious()
: 判断是否存在前一个元素
E previous()
: 返回列表中的前一个元素
以上两个方法可以实现反向遍历,但是注意,要完成反向遍历之前,要先进行正向遍历,这样指针才能移到最后,如果直接反向遍历是没有效果的,因为指针默认位置就在最前面,他前面没有元素。
案例演示
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add(100);
list.add(200);
list.add(300);
list.add(400);
list.add(0, 10);
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()){
System.out.println(listIterator.next());
}
System.out.println("---------------------");
//反向遍历 ,用同一个迭代器,先正向遍历完,才能反向遍历,直接反向遍历没效果,因为指针还在起始位置
while (listIterator.hasPrevious()) {
Object previous = listIterator.previous();
System.out.println(previous);
}
}
}
5.ConcurrentModificationException并发修改异常
案例演示
需求:如果集合中有"world"字符串,那么移除它。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyTest18{
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("good");
list.add("morning");
Iterator iterator = list.iterator();
while (iterator.hasNext()){
String next = (String)iterator.next();
if(next.equals("world")){
list.remove("world");
}
}
System.out.println(list);
}
}
为什么会报ConcurrentModificationException并发修改异常这个异常呢?
这是因为ArrayList
的父类AbstarctList
中有一个域modCount
,每次对集合进行修改(增添元素,删除元素……)时都会modCount++
,在这里,迭代ArrayList
的Iterator
中有一个变量expectedModCount
,该变量会初始化和modCount
相等,但如果接下来如果集合进行修改modCount
改变,就会造成expectedModCount!=modCount
,此时就会抛出java.util.ConcurrentModificationException
异常。(例外:删除倒数第二个元素不会报这个异常)
那要完成这个需求,怎么解决这个问题呢?
- 方式1
不用迭代器遍历
import java.util.ArrayList;
import java.util.List;
public class MyTest{
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("good");
list.add("morning");
//不用迭代器遍历
for (int i = 0; i < list.size(); i++) {
String s =(String)list.get(i);
if(s.equals("world")){
list.remove("world");
}
}
System.out.println(list);
}
}
- 方式2
可以使用迭代器自带的添加/删除方法来解决
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyTest{
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("good");
list.add("morning");
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String s = (String)iterator.next();
if(s.equals("world")){
iterator.remove();//用迭代器的删除方法
}
}
System.out.println(list);
}
}
- 方式3
建一个集合,记录需要删除的元素,之后统一删除
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class MyTest{
public static void main(String[] args) {
List list = new ArrayList();
list.add("hello");
list.add("world");
list.add("good");
list.add("morning");
List list2 = new ArrayList();
Iterator iterator = list.iterator();
while(iterator.hasNext()){
String s = (String)iterator.next();
if(s.equals("world")){
list2.add(s);//将要删除的元素放入新的集合
}
}
list.removeAll(list2);//把list中包含list2的所有元素删除
System.out.println(list);
}
}
思考题:删除集合中的重复元素(不能创建新的集合)
- 方式1
import java.util.ArrayList;
import java.util.List;
public class MyTest{
public static void main(String[] args) {
List list = new ArrayList();
list.add(100);
list.add(200);
list.add(300);
list.add(200);
list.add(400);
list.add(300);
list.add(100);
list.add(200);
list.add(200);
list.add(100);
list.add(200);
for (int i = 0; i < list.size(); i++) {
for(int j=i+1;j<list.size();j++){
if(list.get(i).equals(list.get(j))){
list.remove(j);//按索引删除元素
j--;
}
}
}
System.out.println(list);
}
}
- 方式2
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add(100);
list.add(200);
list.add(300);
list.add(200);
list.add(400);
list.add(300);
list.add(100);
list.add(200);
list.add(200);
list.add(100);
list.add(200);
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
while(list.indexOf(o)!=list.lastIndexOf(o)){
list.remove(list.lastIndexOf(o));
}
}
System.out.println(list);
}
}
- 方式3
递归
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add(100);
list.add(200);
list.add(300);
list.add(200);
list.add(400);
list.add(300);
list.add(100);
list.add(200);
list.add(200);
list.add(100);
list.add(200);
deleteDuplicate(list);
System.out.println(list);
}
private static List deleteDuplicate(List list){
for (int i = 0; i < list.size(); i++) {
Object o = list.get(i);
if(list.indexOf(o)!=list.lastIndexOf(o)){
list.remove(list.lastIndexOf(o));
deleteDuplicate(list);
}
}
return list;
}
}
删除集合中的重复元素(创建新的集合)
import java.util.ArrayList;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add("abc");
list.add("bbb");
list.add("abc");
list.add("abc");
list.add("ccc");
list.add("ddd");
list.add("ccc");
list.add("ddd");
List newList = new ArrayList();
for (int i = 0; i < list.size(); i++) {
String s = (String) list.get(i);
if(!newList.contains(s)){
newList.add(s);
}
}
System.out.println(newList);
}
}
6.排序功能
default void sort(Comparator<? super E> c)
使用提供的 Comparator比较元素
案例演示
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List list = new ArrayList();
list.add(200);
list.add(400);
list.add(500);
list.add(600);
list.add(100);
list.add(20);
//排序集合中的元素
list.sort(new Comparator<Integer>() {
@Override
public int compare(Integer a, Integer b) {
return a-b;//如果要按降序排序就是b-a
}
});
System.out.println(list);
}
}
7.三大子类
List的三个子类的特点
- ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。 - Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。 - LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
(五)ArrayList集合
1.概述
该类也是实现了List的接口,实现了可变大小的数组,随机访问和遍历元素时,提供更好的性能。该类也是非同步的,在多线程的情况下不要使用。插入删除效率低。
2.构造方法
ArrayList()
:构造一个初始容量为 10 的空列表。
ArrayList( int initialCapacity)
:构造一个具有指定初始容量的空列表。
3.新的遍历功能
void forEach (Consumer < ? super E > action)
:执行特定动作的每一个元素的 Iterable直到所有元素都被处理或操作抛出异常。
案例演示1
用forEach对集合进行遍历
import java.util.ArrayList;
import java.util.function.Consumer;
public class MyTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(500);
list.add(100);
list.add(300);
list.add(100);
list.add(500);
list.add(200);
//void forEach (Consumer < ? super E > action)
// Consumer是一个接口,因此需要传它的实现类,我们可以用匿名内部类
list.forEach(new Consumer() {
@Override
public void accept(Object o) {
System.out.println(o);
}
});
}
}
案例演示2
由于Consumer是函数式接口,因此此遍历还有另一种表达方式:
函数式接口:接口中只有一个抽象方法,就称作函数式接口。
如果一个接口是函数式接口,那么你就可以使用 Lambda
表达式来简写它的匿名内部类。
import java.util.ArrayList;
public class MyTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(100);
list.add(100);
list.add(100);
list.add(100);
list.add(500);
list.add(100);
list.add(300);
list.add(100);
list.add(500);
list.add(200);
//括号里代表要传的参数,箭头后代表要实现的逻辑
list.forEach((o)-> System.out.println(o));
}
}
思考题:删除集合中重复的Student对象
import java.util.ArrayList;
import java.util.function.Consumer;
import java.util.Objects;
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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 boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age &&
Objects.equals(name, student.name);
}
}
public class MyTest{
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(new Student("张三",232));
list.add(new Student("张三", 232));
list.add(new Student("张三", 23));
list.add(new Student("张三2", 23));
list.add(new Student("张三3", 23));
list.add(new Student("张三5", 23));
list.add(new Student("张三", 23));
list.add(new Student("张三", 23));
list.add(new Student("张三4", 23));
list.add(new Student("张三7", 23));
list.add(new Student("张三5", 23));
ArrayList newList = new ArrayList();
for (int i = 0; i < list.size(); i++) {
Student student = (Student) list.get(i);
if(!newList.contains(student)){
newList.add(student);
}
}
newList.forEach(new Consumer() {
@Override
public void accept(Object o) {
Student student= (Student) o;
System.out.println(student.getName()+"=="+student.getAge());
}
});
}
}
注意:contains()
方法底层也是用equals()
来判断的,如果不重写自定义类的equals()
方法,那么contains()
方法会用地址值来进行比较而不是比较自定义类的变量的值,就无法达到去重的效果。
(六)Vector集合
1.概述
该类和ArrayList非常相似,但是该类是同步的,可以用在多线程的情况,该类允许设置默认的增长长度,默认扩容方式为原来的2倍。
2.特有功能
public void addElement(E obj)
将指定的组件添加到此向量的末尾,将其大小增加 1
public E elementAt(int index)
返回指定索引处的组件
void insertElementAt (E obj,int index)
将指定对象作为此向量中的组件插入到指定索引处
E lastElement ()
返回此向量的最后一个组件
E firstElement ()
返回此向量的第一个组件(位于索引0处的项)
特有的迭代方式:
public Enumeration elements()
返回此向量的组件的枚举
boolean hasMoreElements()
判断此枚举是否包含更多元素E nextElement()
返回此枚举的下一个元素
案例演示
import java.util.Enumeration;
import java.util.Vector;
public class MyTest {
public static void main(String[] args) {
Vector vector = new Vector();
vector.add(100);
vector.add(100);
vector.add(100);
vector.add(100);
vector.addElement(2000);
vector.addElement(100);
vector.addElement(300);
System.out.println(vector);
vector 特有的迭代方式 elements()获取一个迭代器
Enumeration elements = vector.elements();
while (elements.hasMoreElements()){
Object o = elements.nextElement();
System.out.println(o);
}
System.out.println("=========");
System.out.println(vector.firstElement());
System.out.println(vector.lastElement());
Object o = vector.elementAt(0);
System.out.println(o);
vector.insertElementAt(50, 1); //在指定索引处插入元素
System.out.println(vector);
}
}
(七)LinkedList集合
1.概述
该类实现了List接口,允许有null(空)元素。主要用于创建链表数据结构,该类没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,解决方法就是在创建List时候构造一个同步的List。LinkedList 查找效率低。
2.特有功能
public void addFirst(E e)
将指定元素插入此列表的开头
public void addLast(E e)
将指定元素添加到此列表的结尾
public E getFirst()
返回此列表的第一个元素
public E getLast()
返回此列表的最后一个元素
public E removeFirst()
移除并返回此列表的第一个元素
public E removeLast()
移除并返回此列表的最后一个元素
void push (E e)
将元素推入此列表所表示的堆栈(类似addFirst方法)
E peek()
获取但不移除此列表的第一个元素
E poll()
获取并删除此列表的第一个元素(类似removeFirst方法)
E pop()
删除并返回列表的第一个元素(类似removeFirst方法;类似poll方法)
案例演示
import java.util.LinkedList;
public class MyTest {
public static void main(String[] args) {
// LinkedList 底层数据结构是链表,查询慢,增删快,线程不安装效率高
LinkedList linkedList = new LinkedList();
linkedList.add(100);
linkedList.addLast(1000);
linkedList.add(200);
linkedList.push(90000);
linkedList.addFirst(10);
//push类似于addFirst,最终列表第一个元素由执行顺序决定
System.out.println(linkedList);
//删除并返回列表的第一个元素,类似于poll()
Object pop = linkedList.pop();
System.out.println(pop);
System.out.println(linkedList);
//删除并返回列表的第一个元素
Object poll = linkedList.poll();
System.out.println(poll);
System.out.println(linkedList);
//获取但不移除此列表的第一个元素
Object peek = linkedList.peek();
System.out.println(peek);
System.out.println(linkedList);
}
}
三、泛型
(一)概述
是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。(泛型即参数化类型,把类型当作参数一样的传递)泛型可以用到类、接口、方法上。
(二)泛型的格式
<数据类型>
这里的数据类型只能是引用数据类型
一些常用的泛型类型变量:
E:元素(Element),多用于java集合框架
K:关键字(Key)
N:数字(Number)
T:类型(Type)
V:值(Value)
(三)泛型好处
(1):把运行时期的问题提前到了编译期间(例如:如果添加的数据不是泛型要求的数据类型,编译会报错)编译时就会进行类型检查,保证数据的安全
(2):泛型中的类型在使用时指定,不需要强制类型转换(类型安全,编译器会检查类型)
(3):优化了程序设计,解决了黄色警告线
注意:泛型只在编译期有效 但在运行期就擦除了
案例演示
import java.util.ArrayList;
import java.util.Iterator;
public class MyTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add(100);
list.add("abc");
//集合一般都会使用泛型,明确集合中该添加何种数据类型的元素
ArrayList<Integer> list1 = new ArrayList<>();
list1.add(100);
//list.add("abc");报错,只能添加Integer类型的元素
list1.add(200);
Iterator<Integer> iterator = list1.iterator();
while (iterator.hasNext()){
Integer ele = iterator.next();//泛型的使用,就避免了向下转型
System.out.println(ele);
}
ArrayList<Object> list2 = new ArrayList<>();
list2.add(100);
list2.add("abc");
}
}
(四)泛型类
定义一个泛型类:public class 类名<T>{}
(尖括号内可以有多个类型参数)
class MyObject<M,U> {//泛型类 ,将泛型加在类上面
private M m;
private U u;
public M getM() {
return m;
}
public void setM(M m) {
this.m = m;
}
public U getU() {
return u;
}
public void setU(U u) {
this.u = u;
}
}
public class MyTest{
public static void main(String[] args) {
//泛型,是把类型明确工作,推迟到创建对象或调用方法时,再去明确的一种机制
MyObject<String,Integer> obj = new MyObject<>();
obj.setM("abc");
obj.setU(100);
String m = obj.getM();
Integer u = obj.getU();
System.out.println(m);
System.out.println(u);
}
}
-
父类有泛型,子类可以选择指定泛型类型,也可以选择保留泛型
- 上面子类不保留父类的泛型的第二种:实例化子类对象时,不再需要指明泛型。此时的子类不是泛型类。
- 子类保留父类的泛型的第一种:此时的子类也要加上泛型,并且是泛型类,实例化子类对象时,需要指明泛型。
- 同时子类也可以增加自己的泛型:
-
泛型类在声明空参构造器的时候不能加泛型,但是在利用构造器创建对象的时候需要指明泛型。
-
如果泛型结构是一个接口或者抽象类,则不可以创建泛型类的对象。
-
在类/接口上声明的泛型,在本类或本接口中即代表某种类型, 可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。因为泛型是在创建对象的时候才指明的,而静态方法是先于创建对象执行的。
-
异常类不能是泛型的。
-
定义数组不能写成这样
T[] arr=new T[10];
,要想写只能写成:T[] arr=(T[]) new Object[10];
-
假设类A是类B的父类,但是
T<A>
和T<B>
不具备子父类关系,二者是并列关系。但是A<T>
和B<T>
具备子父类关系,A<T>
是B<T>
的父类,例如List<String>
是ArrayList<String>
的父类
(五)泛型接口
定义一个泛型接口:public interface 接口名<T>{}
(尖括号内可以有多个类型参数)
public interface MyInterface<Y,M,E> {
Y add(M m,E e);
}
- 第一种泛型接口的实现类:
public class 实现类名 implements 接口名<T> {}
(尖括号内可以有多个类型参数)
泛型接口实现类-指定具体类型 实现方式
public class MyTest implements MyInterface<Double,String,Integer>{
@Override
public Double add(String s, Integer i) {
Double a = Double.valueOf(s);
Double sum=a+i;
return sum;
}
public static void main(String[] args) {
MyTest m = new MyTest();
Double add = m.add("125.5", 200);
System.out.println(add);
}
}
- 第二种泛型接口的实现类:
public class 实现类名<T> implements 接口名<T>
(尖括号内可以有多个类型参数)
泛型接口实现类-泛型类 实现方式(泛型接口的实现类也可以是一个泛型类)
public class MyTest<T> implements MyInterface<Double,String,Integer>{
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
@Override
public Double add(String s, Integer i) {
Double a = Double.valueOf(s);
Double sum=a+i;
return sum;
}
public static void main(String[] args) {
MyTest<Integer> m = new MyTest<>();
Double add = m.add("125.5", 200);
System.out.println(add);
m.setT(100);
System.out.println(m.getT());
}
}
- 第三种泛型接口的实现类:匿名内部类
public class MyTest {
public static void main(String[] args) {
//使用匿名内部类创建接口的子类对象时,如果接口是泛型接口,那么需要明确泛型的具体类型
System.out.println(new MyInterface<Double, String, Integer>() {
@Override
public Double add(String s, Integer i) {
Double a = Double.valueOf(s);
Double sum = a + i;
return sum;
}
}.add("234.5", 21445));
}
}
(六)泛型方法
定义一个泛型方法:public <T> 具体返回类型(也可以是泛型) 方法名(T t)
(尖括号内可以有多个类型参数)
class MyObject {
public <T> void show(T t){
System.out.println(t);
}
//有了泛型就不需要每种类型都声明一遍:
//public void show(Integer num){
// System.out.println(num);
//}
//
//public void show(String num) {
// System.out.println(num);
//}
//
//public void show(Double num) {
// System.out.println(num);
//}
}
public class MyTest {
public static void main(String[] args) {
MyObject m = new MyObject();
m.show(124);
m.show("asd");
m.show(155.5);
}
}
- 泛型方法所处的类或接口是不是泛型的,无所谓
- 泛型方法的泛型参数与所处类或接口的泛型参数没有任何关系
- 泛型方法可以声明为静态的,原因:泛型参数是在调用方法时确定的,并非是实例化类时确定的
(七)泛型通配符
(1)泛型通配符<?>
: 任意类型,如果没有明确,那么就是Object以及任意的Java类了
(2)? extends E
: 向下限定,E及其子类,相当于(-∞,E]
(3)? super E
: 向上限定,E及其父类,相当于[E,+∞)
案例演示
import java.util.ArrayList;
class Animal {
}
class Dog extends Animal {
}
class Cat extends Animal {
}
public class MyTest {
public static void main(String[] args) {
//泛型如果明确了数据类型以后,那么要求左右两边的数据类型必须一致
Collection<Object> col1 = new ArrayList<Object>() ;
//Collection<Object> col2 = new ArrayList<Animal>() ;错误的
//未明确数据类型,可以使用泛型通配符,但是不能向其中添加数据,除了null
//如果有别的ArrayList赋值给了此ArrayList<?>,ArrayList<?>里边有数据了,可以通过get方法读取数据,数据类型是Object
ArrayList<?> objects = new ArrayList<Dog>();
//向上限定:类本身及其父类
//但是以下两个集合只能添加Animal及其子类
ArrayList<? super Animal> objects2 = new ArrayList<Object>();
ArrayList<? super Animal> objects3 = new ArrayList<Animal>();
//ArrayList<? super Animal> objects4 = new ArrayList<Dog>(); 错误的
//向下限定:类本身及其子类
//以下三个集合Animal、Dog、Cat、Object类都不能添加
ArrayList<? extends Animal> objects5 = new ArrayList<Animal>();
ArrayList<? extends Animal> objects6 = new ArrayList<Dog>();
ArrayList<? extends Animal> objects7 = new ArrayList<Cat>();
//ArrayList<? extends Animal> objects8 = new ArrayList<Object>(); 错误的
ArrayList<Integer> integers = new ArrayList<>();
integers.add(100);
ArrayList<Integer> integers2 = new ArrayList<>();
integers2.add(10);
ArrayList<String> s1 = new ArrayList<>();
s1.add("abc");
//调用addAll方法的集合如果确定了元素类型,那么addAll方法的参数必须是该元素类型或其子类的集合
//比如此处:addAll(Collection<? extends Integer> c) 该方法要求传入的参数是Integer及其子类的集合
integers.addAll(integers2);
//integers.addAll(s1);错误的
}
}
四、新式for循环
格式:
for(元素数据类型 变量 : 数组或者集合) {
逻辑实现
}
案例演示
import java.util.ArrayList;
import java.util.Iterator;
import java.util.ListIterator;
public class MyTest {
public static void main(String[] args) {
ArrayList<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(4);
list.add(2);
list.add(4);
list.add(3);
list.add(7);
//方式1:传统for循环
// for (int i = 0; i < list.size(); i++) {
// System.out.println(list.get(i));
// }
//方式2:迭代器
// Iterator<Integer> iterator = list.iterator();
// while(iterator.hasNext()){
// System.out.println(iterator.next());
// }
//方式3:子类迭代器
// ListIterator<Integer> integerListIterator = list.listIterator();
// while(integerListIterator.hasNext()){
// System.out.println(integerListIterator.next());
// }
//方式4:foreach
// list.forEach(new Consumer<Integer>() {
// @Override
// public void accept(Integer integer) {
// System.out.println(integer);
// }
// });
//方式5:新式for循环(JDK1.5引入一种新的循环方式,可以用来遍历数组和集合)
//需要做的就两点:1.确定容器中元素的数据类型 2.确定容器的名字
//快捷键:list.for
for (Integer integer : list) {
System.out.println(integer);
}
}
}
那么新式for循环和传统for循环有什么区别呢?
当循环中的逻辑实现需要用到索引时,就用传统for循环;如果不需要用到索引,可以用新式for循环。
注意:使用新式for循环遍历集合的途中,如果你往集合中添加或删除元素时,会报并发修改异常
五、可变参数
当定义方法的时候不知道该定义多少个参数,就可以使用可变参数(可变参数也可以为0个)
案例演示
public class MyTest {
public static void main(String[] args) {
int sum=add();
int sum1 = add(1, 2);
int sum2 = add(1, 2, 3);
int sum3 = add(1, 2, 3, 4);
System.out.println(sum);
System.out.println(sum1);
System.out.println(sum2);
System.out.println(sum3);
}
//可变参数格式: 数据类型 ... 参数名
//可变参数其实就是个数组
private static int add(int... a) { //a是数组名
int sum = 0;
for (int i : a) {
sum += i;
}
return sum;
}
//注意事项:
//如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是写在后面,例如:
// private static int add(int b,int ... a){
// int sum = 0;
// for (int i : a) {
// sum += i;
// }
// return sum+b;
// }
//用可变参数就不需要手动写方法重载了:
//private static int add(int i, int i1) {
// return i+i1;
//}
//
//private static int add(int i, int i1,int i2) {
// return i+i1+i2;
//}
//
//private static int add(int i, int i1,int i2,int i3) {
// return i+i1+i2+i3;
//}
}
六、Arrays工具类的asList()方法
static <T > List < T > asList(T...a)
:将数组转换成集合
案例演示
import java.util.Arrays;
import java.util.List;
public class MyTest {
public static void main(String[] args) {
List<Integer> integer1 = Arrays.asList(100, 200, 300, 400, 500, 600);//把数组转换为集合
System.out.println(integer1);
int[] arr = {1, 2, 3};
Integer[] integer2 = {1, 2, 3};
Integer[] integer3 = {1, 2, 3};
Integer[] integer4 = {1, 2, 3};
//当我们使用 Arrays.asList()这个方法来转换集合的时候,如果我们传入一个或多个int[]数组,
//集合里面放的是int[]数组的引用(泛型只能是引用数据类型)
List<int[]> ints = Arrays.asList(arr, new int[]{10}, new int[]{1, 6, 7});
System.out.println(ints);
//当我们使用 Arrays.asList()这个方法来转换集合的时候,如果我们传入一个Integer[]数组,
//集合里面放的是数组里的元素
List<Integer> integers888 = Arrays.asList(integer2);
System.out.println(integers888);
//当我们使用 Arrays.asList()这个方法来转换集合的时候,如果我们传入多个Integer[]数组,
//集合里面放的是每个Integer[]数组的引用
List<Integer[]> integers999 = Arrays.asList(integer2, integer3, integer4);
System.out.println(integers999);
}
}
注意事项: 得到的集合长度是不可变的,不能往这个转换后的集合中添加元素(add)和删除元素(remove),只能获取元素(get)。
import java.util.Arrays;
import java.util.List;
public class MyTest{
public static void main(String[] args) {
List<Integer> integers = Arrays.asList(100, 200, 300, 400, 500, 600);
//当我们通过 Arrays.asList(100, 200, 300, 400, 500, 600);获取一个转换过来的集合
//那么这个集合的长度是固定的,你只能从里面取出元素用,而不能去增删元素
//integers.remove(0);报错
//integers.add(1);报错
}
}
七、集合嵌套
问题:我们班有学生,每一个学生都是一个对象,所以我们可以使用一个集合ArrayList<Student>
来存储我们班级的学生。但是呢,学校里不止我们一个班级,每个班级都是一个集合ArrayList<Student>
来存储的。那这么多个ArrayList<Student>
,我也想用集合把所有班级存储,怎么办呢?
import java.util.ArrayList;
class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
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 this.name+"==="+this.age;
}
}
public class MyTest{
public static void main(String[] args) {
ArrayList<Student> javaClass = new ArrayList<>();
javaClass.add(new Student("张三",23));
javaClass.add(new Student("李四",22));
javaClass.add(new Student("王五",21));
ArrayList<Student> webClass = new ArrayList<>();
webClass.add(new Student("小明",20));
webClass.add(new Student("小红",20));
webClass.add(new Student("小刚",24));
ArrayList<Student> linuxClass = new ArrayList<>();
linuxClass.add(new Student("赵四",25));
linuxClass.add(new Student("李刚",27));
linuxClass.add(new Student("老王",26));
//集合嵌套
ArrayList<ArrayList<Student>> school = new ArrayList<>();
school.add(javaClass);
school.add(webClass);
school.add(linuxClass);
//遍历1(传统for循环)
for (int i = 0; i < school.size(); i++) {
ArrayList<Student> students = school.get(i);
for (int i1 = 0; i1 < students.size(); i1++) {
Student student = students.get(i1);
System.out.println(student);
}
}
System.out.println("--------------------------");
//遍历2(新式for循环)
for (ArrayList<Student> students : school) {
for (Student student : students) {
System.out.println(student);
}
}
}
}