文章目录
JAVA集合
对象数组
- 案例:
需求:我有3个学生,请把这个3个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。 - 代码示例:(Student类定义)
public 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) { //需求:我有3个学生,请把这个3个学生的信息存储到数组中,并遍历数组,获取得到每一个学生信息。 // 学生:Student // 成员变量:name,age // 构造方法:无参,带参 // 成员方法:getXxx()/setXxx() // 存储学生的数组?自己想想应该是什么样子的? Student s1 = new Student("苏沅湘", 20); Student s2 = new Student("崔季宣", 20); Student s3 = new Student("周颖", 18); //把要把上面三个学生对象存储起来 //创建对象数组 Student[] students={s1,s2,s3}; //遍历对象数组 for (int i = 0; i < students.length; i++) { Student student = students[i]; System.out.println(student.getName()+"==="+student.getAge()); } //删掉数组中的第三个元素 Student[] students1 = Arrays.copyOf(students, 2); System.out.println(Arrays.toString(students1)); } } ---------------------- 输出: 苏沅湘===20 崔季宣===20 周颖===18 [org.westos.demo.Student@1b6d3586, org.westos.demo.Student@4554617c]
- 根据以上案例可以得到的结论:
a. 数组作为容器:可以存储多种多样的类型,但是数组作为容器,增删数组中的元素是很不方便的。
b. 正因为如此,那Java为了我们更方便的去操作(增删改查)容器中的元素,给我们提供了另外一个容器,叫做集合
JAVA集合概述:
-
概念:
Java为了我们更方便的去操作(增删改查)容器中的元素(数组的操作有弊端!),给我们提供了另外一个容器,叫做集合。
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,Java就提供了集合类。 -
数组和集合的区别:
数组:可以存储基本数据类型,也可以存储引用数据类型。
集合:只能存储引用数据类型
数组:一定定义,长度不可变
集合:长度是可变的。
数组:只能存储同一种数据类型
集合:可以存储多种数据类型 -
JAVA中由很多类型的集合,具体可以参考**“Collection集合的继承体系:”**
Collection集合的功能概述:
- Collection 接口 是个单列集合的父类:
示例:public class MyTest { public static void main(String[] args) { // Collection 接口 是个单列集合的父类 //多态(重点),从继承图中可知:ArrayList是Collection的子类! Collection collection = new ArrayList(); //往集合中添加元素,返回值的意思是 是否添加成功 boolean b = collection.add("aaa"); collection.add("bbb"); collection.add("ccc"); System.out.println(collection); } } -------------- 输出: [aaa, bbb, ccc]
- 具体的集合功能可以通过查看API说明手册看到,以下只一部分常用的举例:
- 功能示例:
a. 添加功能
b. 删除功能boolean add(Object obj): 添加一个元素(返回的是是否成功) boolean addAll(Collection c): 添加一个集合的元素 (给一个集合添加进另一个集合中的所有元素) --------------------- 示例: Collection collection1 = new ArrayList(); collection1.add(100); //自动装箱 collection1.add(Integer.valueOf(20)); collection1.add(300); collection1.add(400); //删掉集合中的元素 boolean b = collection1.remove(400); System.out.println(collection1); //清空集合中集合中的元素 collection1.clear(); System.out.println(collection1); 输出: [aaa, bbb, ccc, 100, 20, 300, 400] [100, 20, 300, 400]
c. 判断功能void clear():移除所有元素 boolean remove(Object o):移除一个元素 boolean removeAll(Collection c):移除一个集合的元素(移除一个以上返回的就是true) 删除的元素是两个集合的交集元素,如果没有交集元素 则删除失败 返回false ------------------------ 示例1: Collection collection1 = new ArrayList(); collection1.add(100); //自动装箱(因为集合中存储的是引用类型!,所以自动装箱成Integer类型的数据!) collection1.add(Integer.valueOf(20));//手动装箱 collection1.add(300); collection1.add(400); //删掉集合中的元素 boolean b = collection1.remove(400); System.out.println(collection1); //清空集合中集合中的元素 collection1.clear(); System.out.println(collection1); 输出: [100, 20, 300] [] ------------------- 示例2: Collection collection1 = new ArrayList(); collection1.add(1000); //自动装箱 collection1.add(2000); collection1.add(500); Collection collection2 = new ArrayList(); collection2.add(100); //自动装箱 collection2.add(400); //A集合removeAll(B集合) A 集合会移除掉两个集合的交集元素,如果移除了返回true,没有交集元素返回false boolean b = collection1.removeAll(collection2); System.out.println(b); System.out.println(collection1); System.out.println(collection2); 输出: false [1000, 2000, 500] [100, 400]
d. 获取功能:集合只能通过迭代器进行迭代!boolean contains(Object o):判断集合中是否包含指定的元素 boolean containsAll(Collection c):判断集合中是否包含指定的集合元素(这个集合包含另一个集合中所有的元素才算包含 才返回true),比如:1,2,3 containsAll 12=true 1,2,3 containsAll 2,3,4=false。 boolean isEmpty():判断集合是否为空 -------------------------- 示例1: //与判断相关的方法 Collection collection1 = new ArrayList(); collection1.add(1000); //自动装箱 collection1.add(4000); collection1.add(500); //判断集合中是否有这个元素 boolean b = collection1.contains(500); System.out.println(b); 输出: true ------------------------ 示例2: Collection collection1 = new ArrayList(); collection1.add(1000); //自动装箱 collection1.add(500); //获取集合的长度 int size = collection1.size(); System.out.println(size); collection1.clear(); //判断一个集合是否为空 boolean empty = collection1.isEmpty(); System.out.println(empty); //"".isEmpty(); 字符串也有一个判断是否为空的方法!
e. 长度功能Iterator<E> iterator()(重点) ----------------------- 示例: Collection collection1 = new ArrayList(); collection1.add(1000); //自动装箱 collection1.add(2000); collection1.add(3000); collection1.add(4000); collection1.add(4000); collection1.add(500); //遍历Collection集合需要获取一个迭代器 //对 collection 进行迭代的迭代器 /* boolean hasNext () 如果仍有元素可以迭代,则返回 true。 E next () 返回迭代的下一个元素。*/ // 接口 Iterator<E > Iterator iterator = collection1.iterator(); //java.util.ArrayList$Itr @ 1 b6d3586 Object next = iterator.next();//手动移动 System.out.println(next); System.out.println(iterator); while (iterator.hasNext()){ Object obj= iterator.next(); System.out.println(obj); } System.out.println(collection1); 输出: 1000 java.util.ArrayList$Itr@1b6d3586(这个iterator是一个内部类的对象!(通过打印地址值发现!,但通过追踪源码我无法得出此结论!)) 定义为内部类的好处:可以直接访问集合中的对象!(内部类可以访问外部类的信息!) 2000 3000 4000 4000 500 [1000, 2000, 3000, 4000, 4000, 500] (说明迭代器进行迭代的时候,只是迭代指针发生了变化,而集合本身并没有发生滨化!) ---------------------------------- 示例2:(Student类的定义在本文第一部分已经给出了) Student s1 = new Student("苏沅湘", 20); Student s2 = new Student("崔季宣", 20); Student s3 = new Student("周颖", 18); Collection collection = new ArrayList(); collection.add(s1); collection.add(s2); collection.add(s3); Iterator iterator = collection.iterator(); while (iterator.hasNext()){ Object next = iterator.next(); //向下转型 Student student= (Student) next; System.out.println(student.getName()+"==="+student.getAge()); } ------------------------- 输出: 苏沅湘===20 崔季宣===20 周颖===18
f. 交集功能int size():元素的个数 面试题:数组有没有length()方法呢?字符串有没有length()方法呢?集合有没有length()方法呢? 回答:数组有,字符串有,集合没有(集合是size)。
g. 把集合转换为数组例如:A集合对B集合取交集,获取到的交集元素在A集合中。返回的布尔值表示的是A集合是否发生变化 boolean retainAll(Collection c):获取两个集合的交集元素(交集:两个集合都有的元素) ---------- 示例: //取两个集合的交集元素 Collection collection1 = new ArrayList(); collection1.add(100); //自动装箱 collection1.add(200); collection1.add(300); collection1.add(400); collection1.add(4000); collection1.add(500); Collection collection2 = new ArrayList(); collection2.add(100); //自动装箱 collection2.add(200); collection2.add(300); collection2.add(400); collection2.add(600); //例如:A集合对B集合取交集,获取到的交集元素在A集合中。返回的布尔值表示的是A集合是否发生变化 发生过变化返回true 没有发生变化返回false boolean b = collection1.retainAll(collection2); System.out.println(b); System.out.println(collection1); System.out.println(collection2); 输出: true [100, 200, 300, 400](collection1中只剩下了交集的元素) [100, 200, 300, 400, 600](collection2的元素未发生变化)
Object[] toArray() --------------- 示例: Collection collection1 = new ArrayList(); collection1.add(1000); //自动装箱 collection1.add(2000); collection1.add(3000); collection1.add(4000); collection1.add(4000); collection1.add(500); 把一个集合转换成数组(手动版:) Integer[] integers = new Integer[collection1.size()]; Iterator iterator = collection1.iterator(); int i=0; while (iterator.hasNext()){ Object next = iterator.next(); Integer integer= (Integer) next; integers[i++]=integer; } System.out.println(Arrays.toString(integers)); toArray();把一个集合转换成数组(这个数组是Object型的) Object[] objects = collection1.toArray(); //遍历途中可以对元素进行向下转型(必须向下转型才能获取相应的数值!) for (int j = 0; j < objects.length; j++) { Integer object = (Integer) objects[j]; System.out.println(object.intValue()); } 输出: [1000, 2000, 3000, 4000, 4000, 500] 1000 2000 3000 4000 4000 500
- IDEA小技巧:按住alt操作鼠标,多列选中,对多列同时进行操作!
JAVA集合——list类
List概述及特点
- List概述及特点: 元素有序,并且每一个元素都存在一个索引.元素可以重复.(这里的有序指的是:可以通过索引来获取元素!)
- List集合的特有功能概述:
void add(int index,E element): 在指定索引处添加元素 E remove(int index): 移除指定索引处的元素 返回的是移除的元素 E get(int index): 获取指定索引处的元素 E set(int index,E element): 更改指定索引处的元素 返回的而是被替换的元素
- 根据元素查找索引:
int index = list.indexOf(100); int index2 = list.lastIndexOf(100);
- 遍历集合中的元素:(相当于创建了一个匿名内部类)
list.forEach(new Consumer()){ @Override public void accept(Object o){ System.out.println(o); } }
- 上面遍历的方法可以用Lambda进行简化表示:(简化上面匿名内部类的写法!)
//可以使用Lambda 简化上面的写法 System.out.println("==========================="); list.forEach((obj)->System.out.println(obj));
List的遍历:(案例)
- 案例:List集合遍历(应该有三种方法:两种迭代器,一种for循环)
public class MyTest3 { public static void main(String[] args) { List list = new ArrayList(); list.add(1000); //自动装箱 list.add(2000); list.add(3000); list.add(4000); list.add(4000); list.add(500); //遍历方式1: 利用Colletction的迭代器 //list.iterator() //遍历方式2:List 有一个自己的迭代器 ListIterator listIterator = list.listIterator(); while (listIterator.hasNext()){ Object next = listIterator.next(); System.out.println(next); } System.out.println("=============================="); //遍历方式3 采用for循环遍历 for (int i = 0; i < list.size(); i++) { Object o = list.get(i); System.out.println(o); } } } --------- 输出: 1000 2000 3000 4000 4000 500 ============================== 1000 2000 3000 4000 4000 500
- 案例:List集合存储学生对象并遍历
public class MyTest4 { public static void main(String[] args) { Student s1 = new Student("苏沅湘", 20); Student s2 = new Student("崔季宣", 20); Student s3 = new Student("周颖", 18); List arrayList = new ArrayList(); arrayList.add(s1); arrayList.add(s2); arrayList.add(s3); for (int i = 0; i < arrayList.size(); i++) { Object o = arrayList.get(i); Student student= (Student) o; System.out.println(student.getName()+"==="+ student.getAge()); } } } ------------- 输出: 苏沅湘===20 崔季宣===20 周颖===18
ListIterator的特有功能:
- 概念:ListIterator 继承自Iterator 可以使用Iterator中的方法
- ListIterator的特有功能:
注意:boolean hasPrevious(): 是否存在前一个元素 E previous(): 返回列表中的前一个元素
a. 以上两个方法组合可以实现反向遍历,但是注意:要完成反向遍历之前要先进行正向遍历 ,这样指针才能移到最后
b. 如果直接反向遍历是没有效果的 ,因为指针默认位置就在最前面 他前面没有元素。
List的三个子类的特点:
- List的三个子类的特点:
- ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。 - Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。 - LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
- ArrayList:
- List有三个子集合,我们到底使用谁呢?
要安全还是要效率?
是查找多还是增删多?
(根据以上特性来选择!)
并发修改异常产生的原因及解决方案
- 案例演示:(需求:我有一个集合,我想判断里面有没有 “world” 这个元素,如果有,我就添加一个 “javaee” 元素,请写代码实现。)
public class MyTest2 { public static void main(String[] args) { /* 需求:我有一个集合,请问,我想判断里面有没有 "world" 这个元素, 如果有,我就添加一个 "javaee" 元素,请写代码实现。*/ List list = new ArrayList(); list.add("aaa"); list.add("bbb"); list.add("ccc"); list.add("world"); 方式1: /* if (list.contains("world")) { list.add("javaee"); } System.out.println(list); */ 方式2: // ConcurrentModificationException 并发修改异常 //当你使用迭代器,对集合的元素,进行迭代,那么在迭代途中,先要增删集合中的元素,就会出现并发修改异常 //为什么?是因为,通过集合获取到,这个迭代器之后,迭代器,已经预先知道,集合中的元素个数,会按照既定的元素个数进行迭代,那么在迭代途中,你突然要增删元素,就会打乱迭代器的迭代顺序,所以就会抛出并发修改异常 ListIterator listIterator = list.listIterator(); while (listIterator.hasNext()) { Object next = listIterator.next(); String ele = (String) next; if (ele.equals("world")) { //list.add("javaee"); // 这样的写法会报错 // list.remove("aaa"); // 这样的写法会报错 listIterator.add("javaee"); } } System.out.println(list); //方式3 使用for循环遍历 for (int i = 0; i < list.size(); i++) { if (list.get(i).equals("aaa")) { list.add("vvvv"); } } System.out.println(list); } }
- ConcurrentModificationException 并发修改异常:
当你使用迭代器,对集合元素,进行迭代,那么在迭代途中,如果要增删集合中的元素,就会出现冰法修改异常 - 原因:
因为,通过集合获取到这个迭代器之后,迭代器,已经预先知道,集合总的元素个数,会按照既定的元素个数进行迭代,那么在迭代途中,你突然要增删元素,就会打乱迭代其的迭代顺序,所以就会抛出并发修改异常。 - 解决方式:
解决方式1:(如果使用迭代器遍历,那么在遍历途中,想要增删元素,那么你就使用迭代器自带的增删元素的方法)
解决方式2:使用for循环遍历
数据结构的理解与特性
- 数据结构概述及常见数据结构:
数据结构其实就是存储数据的格式
分类: 栈 , 队列 , 数组 , 链表 , 树 , 哈希表 - 栈特点: 先进后出
- 队列: 先进先出
- 数组特点: 查询快 , 增删慢
- 链表特点: 查询慢 , 增删快
JAVA集合——ArrayList
ArrayList存储与遍历
- JDK1.8新增的一个方法也能遍历集合
void forEach(Consumer<? super E> action) 执行特定动作的每一个元素的 Iterable直到所有元素都被处理或操作抛出异常 - 其他遍历操作:
迭代器(因为ArrayList还是List的子类,可以利用Collection的迭代器或者List独有的迭代器) 普通for(因为ArrayList还是List的子类)
- 示例:
public class MyTest { public static void main(String[] args) { // ArrayList list = new ArrayList(); ArrayList指向(这次与以往不同,不是由Collection或者List指向它,利用多态进行访问了!) /* int lastIndexOf (Object o) 返回此列表中最后一次出现的指定元素的索引,或如果此列表不包含索引,则返回 - 1。 int indexOf (Object o) 返回此列表中首次出现的指定元素的索引,或如果此列表不包含元素,则返回 - 1。 void forEach (Consumer < ? super E > action) 执行特定动作的每一个元素的 Iterable直到所有元素都被处理或操作抛出异常。*/ list.add(100); list.add(200); list.add(300); list.add(100); list.add(400); list.add(500); //根据元素查找改元素第一次出现的索引 int index = list.indexOf(100); System.out.println(index); int i = list.lastIndexOf(100); System.out.println(i); for (int i1 = 0; i1 < list.size(); i1++) { System.out.println(list.get(i1)); } System.out.println("==========================="); //Consumer //遍历集合中的元素 list.forEach(new Consumer() { @Override public void accept(Object o) { System.out.println(o); } }); //可以使用Lambda 简化上面的写法 System.out.println("==========================="); list.forEach((obj)->System.out.println(obj)); } }
案例:(去除ArrayList中的重复数值)
- 示例1:(不采取额外的存储结构)
public class MyTest2 { public static void main(String[] args) { ArrayList list = new ArrayList(); 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(200); list.add(100); list.add(200); list.add(100); list.add(200); list.add(300); list.add(100); list.add(400); list.add(500); list.add(100); list.add(200); list.add(300); list.add(100); list.add(400); list.add(500); list.add(100); 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++) { /* Object o = list.get(i); Object o1 = list.get(j)*/; if(list.get(i).equals(list.get(j))){ list.remove(j); j--;//记得 j--(重点!) } } } System.out.println(list); // HashSet hashSet = new HashSet<>(list); // System.out.println(hashSet); } }
- 示例2:去除集合中字符串的重复值(可以采用辅助的数据结构)
public class MyTest { public static void main(String[] args) { /*A: 案例演示 需求:ArrayList去除集合中字符串的重复值(字符串的内容相同) 思路:创建新集合方式*/ ArrayList list = new ArrayList(); list.add("abc"); list.add("abc"); list.add("abc"); list.add("abc"); list.add("bbb"); list.add("abc"); list.add("ccc"); list.add("aaa"); ArrayList newList = new ArrayList(); for (int i = 0; i < list.size(); i++) { Object o = list.get(i); if(!newList.contains(o)){ newList.add(o); } } System.out.println(newList); } } ------------- 输出: [abc, bbb, ccc, aaa]
- 示例3:(去除自定义类对象的重复值,即:对象的成员变量值相同)
自定义学生类:
主函数:public 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); } @Override public int hashCode() { //非必要重写这部分! return Objects.hash(name, age); } }
引出问题:contains()为何能知道我们比对的标准是什么?public class MyTest2 { public static void main(String[] args) { Student s1 = new Student("张三", 23); Student s2 = new Student("张三", 23); Student s3 = new Student("张三", 23); Student s4 = new Student("张三2", 23); Student s5 = new Student("张三3", 23); ArrayList list = new ArrayList(); list.add(s1); list.add(s2); list.add(s3); list.add(s4); list.add(s5); ArrayList newList = new ArrayList(); for (int i = 0; i < list.size(); i++) { Object o = list.get(i); if(!newList.contains(o)){ 重点在这里,为何它可以对比出不同对象是否是我们要删除的标准? newList.add(o); } } System.out.println("============="); for (int i = 0; i < newList.size(); i++) { Student stu = (Student) newList.get(i); System.out.println(stu.getName()+"==="+stu.getAge()); } } } ------------------- 输出: =============(这里刚好得到我们想要的结果!) 张三===23 张三2===23 张三3===23
追踪contains()源码可知:在ArrayList.java文件下,得到: public boolean contains(Object o) { return indexOf(o) >= 0; } ------------------- 进一步追踪,这里的indexOf()就是ArrayList.java下的indexOF() public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) 发现了重点:它会调用对象的equals()方法进行对比!(恰好我们在自定义的Student类中重写了equals()方法!,所以达到了我们想要的对比效果!) return i; } return -1; }
Java集合——Vector
Vector的概述:
- 概述:Vector 类可以实现可增长的对象数组 , Vector 是同步的。
- 特点:Vector 底层数据结构是数组,查询快,增删慢,线程安全效率低
Vector类特有功能:
- 功能:
public void addElement(E obj) 添加元素的另一种方法 public E elementAt(int index) 获取元素的另一种方法 public Enumeration elements() 返回可迭代的迭代器
- 案例演示:
public class MyTest { public static void main(String[] args) { //Vector 底层数据结构是数组,查询快,增删慢,线程安全效率低 /* public void addElement (E obj) public E elementAt ( int index) public Enumeration elements ()*/ Vector vector = new Vector(); vector.add(200); vector.addElement( 300); vector.add(200); vector.addElement(300); vector.add(200); vector.addElement(300); Object ox = vector.get(0); Object o1 = vector.elementAt(0); System.out.println(o1 == ox); 返回true,则说明这两种取元素的方法效果是一致的! /* lastElement()返回向量的最后一个组件。 firstElement()返回第一个组件(在指数 0项目)这个载体。 removeElement(Object obj)从该向量中移除第一个(最低索引)发生的参数。 void removeElementAt(int index)在指定的索引中删除组件。 setElementAt(E obj, int index)设置组件在指定的index这个向量是指定的对象。 */ //遍历方法: Enumeration elements = vector.elements(); while (elements.hasMoreElements()){ Object o = elements.nextElement(); System.out.println(o); } } } ---------------- 输出: true 200 300 200 300 200 300
JAVA集合——LinkedList
LinkedList概述:
- 概述:List 接口的链接列表实现 , 此实现不是同步的
- 特点:LinkedList 底层数据结构是链表,增删快查询慢,线程不安全效率高
LinkedList特有的功能:
-
功能:
public void addFirst(E e)及addLast(E e) public E getFirst()及getLast() public E removeFirst()及public E removeLast()
-
特有功能示例:
public class MyTest { public static void main(String[] args) { //LinkedList 底层数据结构是链表,增删快查询慢,线程不安全效率高 /* void addFirst(E e)在此列表的开始处插入指定的元素。 void addLast(E e)将指定的元素列表的结束。 public void addFirst(E e)及addLast(E e) public E getFirst()及getLast() public E removeFirst()及public E removeLast() */ LinkedList linkedList = new LinkedList(); linkedList.addLast("eeee"); linkedList.add("aaa"); linkedList.addFirst("bbb"); linkedList.add("ccc"); System.out.println(linkedList); Object o = linkedList.get(0); System.out.println(o); Object first = linkedList.getFirst(); System.out.println(first); Object first2 = linkedList.getFirst(); System.out.println(first2); System.out.println(linkedList); } } ------------- 输出: [bbb, eeee, aaa, ccc] bbb bbb bbb [bbb, eeee, aaa, ccc]
LinkedList案例:(模拟栈的数据结构)
- 示例:(自定义List类)
public class MyList { LinkedList linkedList; public MyList() { linkedList= new LinkedList(); } public void addObj(Object obj) { linkedList.addFirst(obj); } public Object get() { Object obj= linkedList.pop(); linkedList.addLast(obj); //这样做的目的是:操作完后,原来的数据还在!(但个人感觉好像挺没必要的!) return obj; } }
- 示例2:(主函数)
public class MyTest { public static void main(String[] args) { //需求:请用LinkedList模拟栈数据结构的集合,并测试 MyList myList = new MyList(); myList.addObj(100); myList.addObj(200); myList.addObj(300); myList.addObj(400); myList.addObj(500); Object obj=myList.get(); System.out.println(obj); obj = myList.get(); System.out.println(obj); obj = myList.get(); System.out.println(obj); obj = myList.get(); System.out.println(obj); obj = myList.get(); System.out.println(obj); System.out.println("=============="); obj = myList.get(); System.out.println(obj); } } ----------------- 输出: 500 400 300 200 100 ============== 500(这里可以看到,当遍历完一回以后,数据元素没有发生变化!)
JAVA泛型:
概述和基本使用:
- 泛型的由来:
ObjectTool
泛型的由来:通过Object转型问题引入
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。
也就存在这隐患,所以Java提供了泛型来解决这个安全问题。 - 概念:
如果我们使用了泛型,就可以明确集合中到底放了什么样的数据类型。
泛型机制:是JDK1.5引入的,是一种将数据类型明确的工作,推迟到创建对象,或调用方法时,再去明确的一种机制。
也就是:参数化类型,把类型当作参数一样的传递。 - 定义方法:
泛型可以使用在类、接口 方法 <引用类型,引用类型> ------------------ 例如:(在创建ArrayList对象的时候传入指定的泛型为:String,则ArrayList中只能存储这一种类型,若不传入:则默认为:Object类型,所以获取的时候,需要显示地进行向下转换!) ArrayList<String> list1 = new ArrayList<Sring>()
- 泛型的好处:
(1): 把运行时期的问题提前到了编译期间(这句话的含义不太懂?难道是指的避免了运行时候向下转换所造成的报错?这个编译器和运行期到底怎么解释的?编译器不行但是运行期可以(反射)?)
(2): 避免了强制类型转换
(3):优化了程序设计,解决了黄色警告线 - 集合明确了泛型的具体类型,那么这个集合就只能存储这种类型(避免向下转型)
示例:示例:(避免向下转型) 查看上课所述! //集合可以存储多种引用数据类型。 ArrayList list = new ArrayList();(这里没有专门指定泛型的类型声明,则默认为:Object) list.add("abc"); list.add(200); list.add(new StringBuffer()); Object o = list.get(0); String str= (String) o;(向下转型) int length = str.length(); ------------------------- //集合明确了泛型的具体类型,那么这个集合只能存储这种类型 ArrayList<String> list1 = new ArrayList<String>(); list1.add("abc"); list1.add("abc"); list1.add("abc"); String s = list1.get(0); //这里直接获取就OK,不需要向下转型
泛型的好处的演示:(自定义类中使用泛型!)
- 一般通用类型的定义方法:(拥有扩展性,但是必须使用向下转型才能使用!)
public class MyClass { private Object obj; public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } } ---------------------------------------- public class MyTest { public static void main(String[] args) { //定义类时,或方法时,都想考虑这个扩展性 MyClass myClass = new MyClass(); myClass.setObj(100); Object obj = myClass.getObj(); Integer integer= (Integer) obj; // 向下转型 MyClass myClass1 = new MyClass(); myClass1.setObj("abc"); Object obj1 = myClass1.getObj(); String string= (String) obj1; } }
- 泛型定义方法:(既拥有了扩展性,同时也避免了向下转型!)
public class MyDem<T> { //T 泛型(在类中定义!) private T obj; public T getObj() { return obj; } public void setObj(T obj) { this.obj = obj; } } ----------------------------- public class MyTest { public static void main(String[] args) { //设计一个类,既要扩展性,也不想向下转型。 //设计一个泛型类,给类加上泛型 //class A<T>{} //泛型机制:是JDK1.5引入的,是一种将数据类型明确工作,推迟到创建对象,或调用方法时,再去明确的一种机制。 MyDem<String> myDem = new MyDem<>(); 由于前面已经声明了泛型的指代为:String,后面的<>中的泛型指定可以省略。 myDem.setObj("abc"); String obj = myDem.getObj(); System.out.println(obj); //如不注明,则说明:泛型为Object! MyDem<Integer> integerMyDem = new MyDem<>(); integerMyDem.setObj(200); Integer obj1 = integerMyDem.getObj(); new ArrayList<Integer>(); //指明了ArrayList中泛型为:Integer,则ArrayList中只能存储Integer类型的数据。 } }
多泛型的使用:
- 示例:(定义泛型的类)
public class MyHaHa<R,P>{ //多泛型 private R r; private P p; }
- 示例:(主函数)
public class MyTest { public static void main(String[] args) { MyHaHa<Integer, String> integerStringMyHaHa = new MyHaHa<>(); //多泛型的使用! // integerStringMyHaHa.set("string"); ArrayList<Integer> integers = new ArrayList<>(); integers.add(100); integers.add(100); integers.add(100); integers.add(100); integers.add(100); integers.add(100); integers.forEach(new Consumer<Integer>() { @Override public void accept(Integer integer) { System.out.println(integer); } }); } } ---- 输出: 100 100 100 100 100 100
泛型接口的概述和使用
- 接口上的泛型都是在定义实现其子类的过程中传入的!
- 示例:(主函数中利用匿名内部类来实现接口的子类)
public class MyTest { public static void main(String[] args) { //泛型接口 什么时候去明确 //匿名内部类 new MyInterface<Integer,Integer>(){ @Override public Integer set(Integer integer) { return null; } }; } }
- 示例:(接口)
interface MyInterface<T,R>{ //在接口上使用泛型 R set(T t); }
- 示例:(实现接口的子类)
//子类在实现接口时,可以以明确接口上的泛型到底是什么类型 class Son implements MyInterface<String,Integer>{ @Override public Integer set(String s) { return null; } }
泛型在方法上的使用:
- 示例:(自定义类)
class AA{
原来的办法:(为了考虑通用性)
/* public void set(Object str) {
System.out.println(str);
}*/
泛型方法
public<P> void set(P str) {
System.out.println(str);
}
}
- 主函数:
public class MyTest { public static void main(String[] args) { //方法上的泛型,在你调用方法时,再去明确 AA aa = new AA(); aa.set(3.14); //这里没有专门指定泛型的语法,确也可以(神奇!!!!) aa.set(200); aa.set("abc"); } }
- 泛型一般在类和接口上使用的比较多!
泛型高级之通配符:
-
泛型通配符<?>:
任意类型,如果没有明确,那么就是Object以及任意的Java类了 -
泛型如果明确了数据类型以后,那么要求左右两边的数据类型必须一致
Collection<Object> col1 = new ArrayList<Object>() ; Collection<Object> col2 = new ArrayList<Animal>() ; 报错(前后不一致)
-
泛型统配符号:(前面用统配符号,后面则必须注明!)
ArrayList<?> objects2 = new ArrayList<cat>(); 这种方法就不需要前后都一致了!
-
? extends E:向下限定,E及其子类
Collection<? extends Animal> col9 = new ArrayList<Object>() ; 报错(因为Object不是Animal的子类) Collection<? extends Animal> col10 = new ArrayList<Animal>() ; 自己本身也可以 Collection<? extends Animal> col11 = new ArrayList<Dog>() ;
-
? super E:向上限定,E及其父类
Collection<? super Animal> col13 = new ArrayList<Object>() ; Collection<? super Animal> col14 = new ArrayList<Animal>() ; Collection<? super Animal> col15 = new ArrayList<Dog>() ; 报错(因为Dog是Animal的子类而不是父类!)
-
特别注意:泛型通配符的一些注意要点,这里面说明了这些泛型通配符在什么场景下使用,该如何使用,有哪些需要注意的坑!(非常重要!)
增强for循环:
- 概述:主要用来简化数组和Collection集合的遍历(在JDK1.5及以后版本适用)
- 格式:
for(元素数据类型 变量 : 数组或者Collection集合) { 使用变量即可,该变量就是元素 }
- 示例:(遍历数组)
int[] arr={20,20,30,80}; --------------------(普通for循环) for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } --------------------(增强for循环) for(int i:arr){ System.out.println(arr[i]); }
- 示例:(遍历集合)
ArrayList<Integer> list = new ArrayList<>(); list.add(200); list.add(200); list.add(200); list.add(200); for(Integer num:list){ System.out.println(num); }
- 优点:简化了遍历的写法!
- 特点:它的底层是用迭代器在进行迭代,所以在迭代过程中不能进行增删元素!),这一点和Python极其像!
否则,就会报错:java.util.ConcurrentModificationException (并发修改异常!) - 普通for循环和增强for循环的适用特点:
a. 如果循环过程中需要拿索引做判断,那你就使用普通for循环
b. 如果只是遍历元素看一下,那就使用新式for循环
JAVA可变参数:
-
可变参数概述:
定义方法的时候不知道该定义多少个参数(如果也不知道具体类型的话,则可以使用泛型来进行指定!),可变参数(数组)中的数据类型都是一样的(是同属一个类型的数组!) -
格式:
修饰符 返回值类型 方法名(数据类型… 变量名){}
-
注意:
a. 这里的变量其实是一个数组
b. 如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个 -
示例:
public class MyTest { public static void main(String[] args) { int sum = add(1, 2); int sum2 = add(1, 2, 3); int sum3 = add(1, 2, 3, 4); //可变参数:一次可以接收多个参数 System.out.println(sum); System.out.println(sum2); System.out.println(sum3); } //可变参数 数据类型 ... 参数名 //可变参数 本质是个数组 //如果说一个方法上,有普通参数,也有可变参数,可变参数肯定是最后一个参数 private static int add(int b,int... num) { //System.out.println(num.length); int sum=0; for (int i : num) { // 增强for循环! sum+=i; } return sum+b; } }
Arrays工具类的asList()方法的使用:
- 概念:Arrays工具类的asList(T… t)方法的使用,将数组转换成集合。
- 格式:
static <T> List < T > asList(T...a)
- 示例:
public class MyTest { public static void main(String[] args) { Integer[] integers = {20, 30, 50, 60}; 如果你给该方法,传入的是一个引用类型数组,那么他会取出数组中的元素,放到集合中 List<Integer> integers1 = Arrays.asList(integers); for (Integer integer : integers1) { System.out.println(integer); } System.out.println("====================================="); Integer[] integers2 = {20, 30, 50, 60}; Integer[] integers3 = {20, 30, 50, 60}; 假如你传入两个以上的数组,他是把数组对象作为元素,放到集合中 List<Integer[]> integers4 = Arrays.asList(integers, integers2); System.out.println(integers4); Integer integer = integers4.get(0)[0]; System.out.println(integer); System.out.println("================"); Integer[] integers5 = {20, 30, 50, 60}; List<Integer> integers6 = Arrays.asList(integers5); int[] ints = {20, 30, 50, 60}; 当你传人一个基本类型数组,那么转换成集合,集合放到是数组对象(数组对象的指针) List<int[]> ints1 = Arrays.asList(ints); int[] ints2 = ints1.get(0); int[] ints3 = {20, 30, 50, 60}; int[] ints4 = {20, 30, 50, 60}; List<int[]> ints8 = Arrays.asList(ints3, ints4); System.out.println(ints8); List<Integer> integers7 = Arrays.asList(20, 30, 50, 30, 80); //自动装箱! System.out.println(integers7); } } ------ 输出: 20 30 50 60 ===================================== [[Ljava.lang.Integer;@1b6d3586, [Ljava.lang.Integer;@4554617c] 20(第0个元素) ================ [[I@74a14482, [I@1540e19d](List中存储的是int[]对象的地址值!) [20, 30, 50, 30, 80]
- 特别注意:得到的集合长度是不可变的。你不能往这个转换后的集合中添加元素(add)和删除元素(remove),只能获取元素(get)。
JAVA 嵌套集合:(ArrayList嵌套ArrayList)
- 示例:
public class MyTest3 { public static void main(String[] args) { ArrayList<Student> javaList = new ArrayList<>(); javaList.add(new Student("周丹", 19)); javaList.add(new Student("周颖", 19)); javaList.add(new Student("苏沅湘", 20)); javaList.add(new Student("崔继宣", 19)); ArrayList<Student> webList = new ArrayList<>(); webList.add(new Student("周丹1", 19)); webList.add(new Student("周颖1", 19)); webList.add(new Student("苏沅湘1", 20)); webList.add(new Student("崔继宣1", 19)); ArrayList<Student> linuxList = new ArrayList<>(); linuxList.add(new Student("周丹2", 19)); linuxList.add(new Student("周颖2", 19)); linuxList.add(new Student("苏沅湘2", 20)); linuxList.add(new Student("崔继宣2", 19)); //我站在西开的角度,我要存储这个三个班(嵌套ArrayList存储) ArrayList<ArrayList<Student>> westosList = new ArrayList<>(); westosList.add(javaList); westosList.add(webList); westosList.add(linuxList); //遍历每一个班的每一个学生的信息 二维集合的遍历 //普通for循环遍历 for (int i = 0; i < westosList.size(); i++) { ArrayList<Student> minList = westosList.get(i); // System.out.println(minList); for (int j = 0; j < minList.size(); j++) { Student student = minList.get(j); System.out.println(student.getName() + "===" + student.getAge()); } } System.out.println("================================="); //新式for循环遍历 for (ArrayList<Student> students : westosList) { for (Student student : students) { System.out.println(student.getName() + "===" + student.getAge()); } } System.out.println("======================"); } } --------------- 输出: 周丹===19 周颖===19 苏沅湘===20 崔继宣===19 周丹1===19 周颖1===19 苏沅湘1===20 崔继宣1===19 周丹2===19 周颖2===19 苏沅湘2===20 崔继宣2===19 ================================= 周丹===19 周颖===19 苏沅湘===20 崔继宣===19 周丹1===19 周颖1===19 苏沅湘1===20 崔继宣1===19 周丹2===19 周颖2===19 苏沅湘2===20 崔继宣2===19 ======================