1:List的子类(掌握)
(1)List的子类特点
ArrayList:
底层数据结构是数组,查询快,增删慢
线程不安全,效率高
Vector:
底层数据结构是数组,查询快,增删慢
线程安全,效率低
LinkedList:
底层数据结构是链表,查询慢,增删快
线程不安全,效率高
(2)ArrayList
A:没有特有功能需要学习
B:案例
a:ArrayList存储字符串并遍历
b:ArrayList存储自定义对象并遍历
public class ArrayListDemo {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建元素对象,并添加元素
array.add("hello");
array.add("world");
array.add("java");
// 遍历
Iterator it = array.iterator();
while (it.hasNext()) {
String s = (String) it.next();
System.out.println(s);
}
System.out.println("-----------");
for (int x = 0; x < array.size(); x++) {
String s = (String) array.get(x);
System.out.println(s);
}
}
}
/*
* ArrayList存储自定义对象并遍历
*/
public class ArrayListDemo2 {
public static void main(String[] args) {
// 创建集合对象
ArrayList array = new ArrayList();
// 创建学生对象
Student s1 = new Student("武松", 30);
Student s2 = new Student("鲁智深", 40);
Student s3 = new Student("林冲", 36);
Student s4 = new Student("杨志", 38);
// 添加元素
array.add(s1);
array.add(s2);
array.add(s3);
array.add(s4);
// 遍历
Iterator it = array.iterator();
while (it.hasNext()) {
Student s = (Student) it.next();
System.out.println(s.getName() + "---" + s.getAge());
}
System.out.println("----------------");
for (int x = 0; x < array.size(); x++) {
// ClassCastException 注意,千万要搞清楚类型
// String s = (String) array.get(x);
// System.out.println(s);
Student s = (Student) array.get(x);
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
(3)Vector:Vector
类可以实现可增长的对象数组。与数组一样,它包含可以使用整数索引进行访问的组件。但是,Vector
的大小可以根据需要增大或缩小,以适应创建 Vector
后进行添加或移除项的操作。
A:有特有功能
a:添加
public void addElement(E obj) -- add()
b:获取
public E elementAt(int index) -- get()
public Enumeration<E> elements() -- iterator()
B:案例
a:Vector存储字符串并遍历
b:Vector存储自定义对象并遍历
有特有功能例子
/*
* Vector的特有功能:
* 1:添加功能
* public void addElement(Object obj) -- 与 add()相似
* 2:获取功能
* public Object elementAt(int index) -- 与 get()相似
* public Enumeration elements() -- 与 Iterator iterator()相似
* boolean hasMoreElements()----- 与 hasNext()相似
* Object nextElement() ------- 与 next()相似
*
* JDK升级的原因:
* A:安全
* B:效率
* C:简化书写
*/
public class VectorDemo {
public static void main(String[] args) {
// 创建集合对象
Vector v = new Vector();
// 添加功能
v.addElement("hello");
v.addElement("world");
v.addElement("java");
// 遍历
for (int x = 0; x < v.size(); x++) {
String s = (String) v.elementAt(x);
System.out.println(s);
}
System.out.println("------------------");
Enumeration en = v.elements(); // 返回的是实现类的对象
while (en.hasMoreElements()) {
String s = (String) en.nextElement();
System.out.println(s);
}
}
}
(4)LinkedList :双向链表
A:有特有功能
a:添加
addFirst()
addLast()
b:删除
removeFirst()
removeLast()
c:获取
getFirst()
getLast()
B:案例
a:LinkedList存储字符串并遍历
b:LinkedList存储自定义对象并遍历
特有功能例子:
/*
* LinkedList的特有功能:
* A:添加功能
* public void addFirst(Object e)
* public void addLast(Object e)
* B:获取功能
* public Object getFirst()
* public Obejct getLast()
* C:删除功能
* public Object removeFirst()
* public Object removeLast()
*/
public class LinkedListDemo {
public static void main(String[] args) {
// 创建集合对象
LinkedList link = new LinkedList();
// 添加元素
link.add("hello");
link.add("world");
link.add("java");
//public void addFirst(Object e)
link.addFirst("javaee");
//public void addLast(Object e)
link.addLast("android");
//public Object getFirst()
System.out.println("getFirst:" + link.getFirst());
//public Obejct getLast()
System.out.println("getLast:" + link.getLast());
// public Object removeFirst()
System.out.println("removeFirst:" + link.removeFirst());
// public Object removeLast()
System.out.println("removeLast:" + link.removeLast());
// 输出对象名
System.out.println("link:" + link);
}
}
(5)案例:(见本博客随笔文章:List集合类案例)
A:去除集合中的多个字符串的重复元素
如果字符串的内容相同,即为重复元素
B:去除集合中的多个自定义对象的重复元素
如果自定义对象的成员变量值都相同,即为重复元素
C:用LinkedList模拟一个栈数据结构的集合类,并测试。
你要定义一个集合类,只不过内部可以使用LinkedList来实现。
JDK5新特性:自动装拆箱,泛型,增强for,静态导入,可变参数,枚举
2:泛型(掌握) 注意:泛型默认把<E>或<T>去掉时,默认引用类型为Object 例如:ArrayList al = new ArrayList(); //默认类型为Object
泛型最主要的作用在我看来就是把Object向下转型时类型确定,因为不确定泛型(object在强转是会报错),但要到编译时才发现错,而设定泛型,能把错误提前到编写代码阶段。
(1)泛型概述
是一种把类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型。参数化类型,把类型当作参数一样的传递。
(2)格式:
<e>=<数据类型>
注意:该数据类型只能是引用类型。
// JDK7的新特性:泛型推断。但不建议使用,因为写法不普及
// ArrayList<Student> array = new ArrayList<>(); //只需在设置变量是确定,在构造对象时自动推断泛型的引用类型,但不建议使用,因为写法不普及
(3)好处:
A:把运行时期的问题提前到了编译期间 (没有泛型,会由于不同类型都同时存入集合中,读取出来时由于不知道不同类型的特性,强转类型会报错),基本类型也能进入集合,本来是不行了,但JDK5新增特性自动装箱,搞的自动转型也能存进集合,为了限制集合中类型的种类或保持类型一致,所以引入泛型。
B:避免了强制类型转换 (设置了Iterator的泛型后,不用强制转换,直接String s = it.next();)
C:优化了程序设计,解决了黄色警告线问题,让程序更安全
(4)泛型的前世今生
A:泛型的由来
Object类型作为任意类型的时候,在向下转型的时候,会隐含一个转型问题
早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
B.泛型类
把泛型定义在类上
格式:public class 类名<泛型类型1,…>
注意:泛型类型必须是引用类型
C.泛型方法
把泛型定义在方法上
格式:public <泛型类型> 返回类型 方法名(泛型类型 .)
D.泛型接口
把泛型定义在接口上
格式:public interface 接口名<泛型类型1…> 最后也会向泛型类靠拢
E:泛型高级通配符(在专门一篇随笔有所体现)
?:任意类型,如果没有明确,那么就是Object以及任意的Java类了,不能执行添加,只能执行get
? extends E:向上限定,E及其子类,不能执行添加,只能执行get
? super E:向下限定,E极其父类,只能添加,不能get(系统报错)
(1)带有超类型限定的通配符可以向泛型对象写入;?super T
(2)带有子类型限定的通配符可以从泛型对象读取。?extends T
在Java集合框架中,对于参数值是未知类型的容器类,只能读取其中元素,不能向其中添加元素,
因为,其类型是未知,所以编译器无法识别添加元素的类型和容器的类型是否兼容,唯一的例外是NULL
(5)我们在哪里使用呢?
一般是在集合(Collection)中使用。
/** 泛型类:把泛型定义在类上*/
public class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
/** 泛型类的测试*/
public class ObjectToolDemo {
public static void main(String[] args) {
ObjectTool ot = new ObjectTool();
ot.setObj(new String("风清扬"));
String s = (String) ot.getObj();
System.out.println("姓名是:" + s);
ot.setObj(new Integer(30)); //这个时候编译期间能过去,但是黄色警告没有消失
Integer i = (Integer) ot.getObj();
System.out.println("年龄是:" + i);
ot.setObj(new String("林青霞"));
// ClassCastException
Integer ii = (Integer) ot.getObj(); //这个时候编译期间就过不去,类转换类型报错
System.out.println("姓名是:" + ii);
System.out.println("-----------------------------------------------------------------");
ObjectTool<String> ot = new ObjectTool<String>();
// ot.setObj(new Integer(27)); //这个时候编译期间就过不去
ot.setObj(new String("林青霞"));
String s = ot.getObj();
System.out.println("姓名是:" + s);
ObjectTool<Integer> ot2 = new ObjectTool<Integer>();
// ot2.setObj(new String("风清扬"));//这个时候编译期间就过不去
ot2.setObj(new Integer(27));
Integer i = ot2.getObj();
System.out.println("年龄是:" + i);
}
}
/** 泛型方法:把泛型定义在方法上*/
public class ObjectTool {
public <T> void show(T t) {
System.out.println(t);
}
}
public class ObjectToolDemo {
public static void main(String[] args) {
// 如果还听得懂,那就说明泛型类是没有问题的
// 但是呢,谁说了我的方法一定要和类的类型的一致呢?
// 我要是类上没有泛型的话,方法还能不能接收任意类型的参数了呢?
// 定义泛型方法后
ObjectTool ot = new ObjectTool();
ot.show("hello");
ot.show(100);
ot.show(true);
}
}
泛型接口:
//实现类在实现接口的时候
//第一种情况:已经知道该是什么类型的了
public class InterImpl implements Inter<String> {
@Override
public void show(String t) {
System.out.println(t);
}
}
//第二种情况:还不知道是什么类型的
public class InterImpl<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
public class InterDemo {
public static void main(String[] args) {
// 第一种情况的测试
// Inter i = new InterImpl();
// i.show("hello");
// // 第二种情况的测试
Inter<String> i = new InterImpl<String>();
i.show("hello");
Inter<Integer> ii = new InterImpl<Integer>();
ii.show(100);
}
}
3:增强for循环(掌握)
(1)是for循环的一种:凡数组和实现Iterable接口的类都可以使用增强for循环。
(2)格式:
for(元素的数据类型 变量名 : 数组或者Collection集合的对象) {
使用该变量即可,该变量其实就是数组或者集合中的元素。
}
(3)好处:
简化了数组和集合的遍历
(4)弊端
增强for循环的目标不能为null。建议在使用前,先判断是否为null。
// 增强for其实是用来替代迭代器的,通过反编译文件就可以看到,所以也会有迭代器的并发修改异常
反编译对于增强for的代码:for (Iterator iterator = array.iterator(); iterator.hasNext(); System.out.println((new StringBuilder(String.valueOf(s.getName()))).append("---").append(s.getAge()).toString()))
4:静态导入(了解)
(1)可以导入到方法级别的导入
(2)格式:
import static 包名....类名.方法名;
import static java.lang.Math.abs;
import static java.lang.Math.pow;
import static java.lang.Math.max;
使用的时候,没重名时:abs(-100);(因为静态设定了abs方法的路径,如果重名就不行,所以很少人用)
(3)注意事项:
A:方法必须是静态的
B:如果多个类下有同名的方法,就不好区分了,还得加上前缀。(java.lang.Math.abs(-100))
所以一般我们并不使用静态导入,但是一定要能够看懂。
5:可变参数(掌握)
(1)如果我们在写方法的时候,参数个数不明确,就应该定义可变参数。
(2)格式:"..."三个点就是格式
修饰符 返回值类型 方法名(数据类型... 变量) {}
public static int sum(int... a)
注意:
A:该变量其实是一个数组名,其实可变参数就是数组,不过数组的长度是在编译的时候固定的
B:如果一个方法有多个参数,并且有可变参数,可变参数必须在最后
(3)Arrays工具类的一个方法: public static <T> List<T> asList(T... a) 这个方法就是可变参数:List<String> stooges = Arrays.asList("Larry", "Moe", "Curly");
asList()把数组转成集合。API解释:返回一个受指定数组支持的固定大小的列表。
注意:这个集合的长度不能改变。因为这个集合是基于数组来定的,数组是不能变更长度,但这个集合的内容是可以修改的。
例子:
public class Demo01 {
// 这里的变量其实是一个数组
// 如果一个方法有可变参数,并且有多个参数,那么,可变参数肯定是最后一个
// 需求:我要写一个求和的功能,到底是几个数据求和呢,我不太清楚,但是我知道在调用的时候我肯定就知道了,编译的时候固定长度,编译前可变
// 为了解决这个问题,Java就提供了一个东西:可变参数
public static void main(String[] args) {
// TODO Auto-generated method stub
int sum1 = sum(1, 2, 3, 4);
System.out.println(sum1);
int sum2 = sum(8, 9, 775, 8, 77);
System.out.println(sum2);
// 带有固定参数与可变参数
int sum3 = sum("str001", 25, 84, 77);
System.out.println(sum3);
}
public static int sum(int... a) {
int sum = 0;
for (int s : a) {
sum += s;
}
return sum;
}
// 带有固定参数与可变参数,可变参数放最后,因为不知道可变参数的长度有多长,固定参数与可变参数类型相同时,固定参数放在后面容易混乱
public static int sum(String b, int... a) {
int sum = 0;
for (int s : a) {
sum += s;
}
return sum;
}
}
6:练习(掌握)
A:集合的嵌套遍历
B:产生10个1-20之间的随机数,要求随机数不能重复
C:键盘录入多个数据,以0结束,并在控制台输出最大值
A:集合的嵌套遍历
public class ArrayListDemo {
public static void main(String[] args) {
// 创建大集合
ArrayList<ArrayList<Student>> bigArrayList = new ArrayList<ArrayList<Student>>();
// 创建第一个班级的学生集合
ArrayList<Student> firstArrayList = new ArrayList<Student>();
// 创建学生
Student s1 = new Student("唐僧", 30);
Student s2 = new Student("孙悟空", 29);
Student s3 = new Student("猪八戒", 28);
Student s4 = new Student("沙僧", 27);
Student s5 = new Student("白龙马", 26);
// 学生进班
firstArrayList.add(s1);
firstArrayList.add(s2);
firstArrayList.add(s3);
firstArrayList.add(s4);
firstArrayList.add(s5);
// 把第一个班级存储到学生系统中
bigArrayList.add(firstArrayList);
// 创建第二个班级的学生集合
ArrayList<Student> secondArrayList = new ArrayList<Student>();
// 创建学生
Student s11 = new Student("诸葛亮", 30);
Student s22 = new Student("司马懿", 28);
Student s33 = new Student("周瑜", 26);
// 学生进班
secondArrayList.add(s11);
secondArrayList.add(s22);
secondArrayList.add(s33);
// 把第二个班级存储到学生系统中
bigArrayList.add(secondArrayList);
// 创建第三个班级的学生集合
ArrayList<Student> thirdArrayList = new ArrayList<Student>();
// 创建学生
Student s111 = new Student("宋江", 40);
Student s222 = new Student("吴用", 35);
Student s333 = new Student("高俅", 30);
Student s444 = new Student("李师师", 22);
// 学生进班
thirdArrayList.add(s111);
thirdArrayList.add(s222);
thirdArrayList.add(s333);
thirdArrayList.add(s444);
// 把第三个班级存储到学生系统中
bigArrayList.add(thirdArrayList);
// 遍历集合
for (ArrayList<Student> array : bigArrayList) {
for (Student s : array) {
System.out.println(s.getName() + "---" + s.getAge());
}
}
}
}
B:产生10个1-20之间的随机数,要求随机数不能重复
public class RandomDemo {
public static void main(String[] args) {
// 创建产生随机数的对象
Random r = new Random();
// 创建一个存储随机数的集合。
ArrayList<Integer> array = new ArrayList<Integer>();
// 定义一个统计变量。从0开始。
int count = 0;
// 判断统计遍历是否小于10
while (count < 10) {
//先产生一个随机数
int number = r.nextInt(20) + 1;
//判断该随机数在集合中是否存在。
if(!array.contains(number)){
//如果不存在:就添加,统计变量++。
array.add(number);
count++;
}
}
//遍历集合
for(Integer i : array){
System.out.println(i);
}
}
}
C:键盘录入多个数据,以0结束,并在控制台输出最大值
public class ArrayListDemo {
public static void main(String[] args) {
// 创建键盘录入数据对象
Scanner sc = new Scanner(System.in);
// 键盘录入多个数据,我们不知道多少个,所以用集合存储
ArrayList<Integer> array = new ArrayList<Integer>();
// 以0结束,这个简单,只要键盘录入的数据是0,我就不继续录入数据了
while (true) {
System.out.println("请输入数据:");
int number = sc.nextInt();
if (number != 0) {
array.add(number);
} else {
break;
}
}
// 把集合转成数组
// public <T> T[] toArray(T[] a)
Integer[] i = new Integer[array.size()];
// Integer[] ii = array.toArray(i);
array.toArray(i);
// System.out.println(i);
// System.out.println(ii);
// 对数组排序
// public static void sort(Object[] a)
Arrays.sort(i);
// 获取该数组中的最大索引的值
System.out.println("数组是:" + arrayToString(i) + "最大值是:"
+ i[i.length - 1]);
}
public static String arrayToString(Integer[] i) {
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int x = 0; x < i.length; x++) {
if (x == i.length - 1) {
sb.append(i[x]);
} else {
sb.append(i[x]).append(", ");
}
}
sb.append("]");
return sb.toString();
}
}
7:要掌握的代码
集合存储元素,加入泛型,并可以使用增强for遍历。