04 集合
###04.01 集合的概述
* A:集合的由来* 数组长度是固定,当添加的元素超过了数组的长度时需要对数组重新定义,太麻烦,java内部给我们提供了集合类,能存储任意对象,长度是可以改变的,随着元素的增加而增加,随着元素的减少而减少
* B:数组和集合的区别
* 区别1 :
* 数组既可以存储基本数据类型,又可以存储引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值
* 集合只能存储引用数据类型(对象)。集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象
* 区别2:
* 数组长度是固定的,不能自动增长
* 集合的长度的是可变的,可以根据元素的增加而增长
* C:数组和集合什么时候用
* 1,如果元素个数是固定的推荐用数组
* 2,如果元素个数不是固定的推荐用集合
###04.02 List的三个子类的特点
ArrayList:
底层数据结构是数组,查询快,增删慢。
线程不安全,效率高。
Vector:
底层数据结构是数组,查询快,增删慢。
线程安全,效率低。
Vector相对ArrayList查询慢(线程安全的)
Vector相对LinkedList增删慢(数组结构)
LinkedList:
底层数据结构是链表,查询慢,增删快。
线程不安全,效率高。
Vector和ArrayList的区别:
Vector是线程安全的,效率低
ArrayList是线程不安全的,效率高
共同点:都是数组实现的
ArrayList和LinkedList的区别:
ArrayList底层是数组结果,查询和修改快
LinkedList底层是链表结构的,增和删比较快,查询和修改比较慢
共同点:都是线程不安全的
###04.03 迭代器的原理
迭代器原理
迭代器原理:迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义hasNext()和next()方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取出接口,然后在每个类的内部,定义自己迭代方式,
这样做的好处有二, 第一:规定了整个集合体系的遍历方式都是hasNext()和next()方法;第二:代码有底层内部实现,使用者不用管怎么实现的,会用即可
###04.04 List集合的特有功能
List集合的特有功能概述
* void add(int index,E element) //index<= size且 >=0都不会异常
* E remove(int index) // 通过索引删除元素,删除的时候不会自动装箱
List lis = new ArrayList();
lis.add(111); //添加的时候会自动装箱。
lis.add(222);
lis.add(333);
lis.remove(1); //是删除索引1处的元素,索引1处的元素是222.
* E get(int index) //获得指定位置处的索引
* E set(int index,E element) //将指定位置的元素修改
size()和get()方法结合使用遍历。
List list = new ArrayList();
list.add(new Student("张三", 18));
list.add(new Student("李四", 18));
list.add(new Student("王五", 18));
list.add(new Student("赵六", 18));
for(int i = 0; i < list.size(); i++) {
Student s = (Student)list.get(i);
System.out.println(s.getName() + "," + s.getAge());
}
###04.05 集合框架(Vector的特有功能)
* A:Vector类概述
* B:Vector类特有功能
* public void addElement(E obj)
* public E elementAt(int index)
* public Enumeration elements()
* C:案例演示
* Vector的迭代
Vector v = new Vector(); //创建集合对象,List的子类
v.addElement("a");
v.addElement("b");
v.addElement("c");
v.addElement("d");
//Vector迭代
Enumeration en = v.elements(); //获取枚举
while(en.hasMoreElements()) { //判断集合中是否有元素
System.out.println(en.nextElement());//获取集合中的元素
}
###04.06 泛型概述和基本使用
* A:泛型概述
* B:泛型好处
* 提高安全性(将运行期的错误转换到编译期)
* 黄线消失
* 省去强转的麻烦
* C:泛型基本使用
* <>中放的必须是引用数据类型
* D:泛型使用注意事项
* 前后的泛型必须一致,或者后面的泛型可以省略不写(1.7的新特性菱形泛型)
* 若定义Object类型可以接收任意类型,无意义,所以一般不定义成Object类型。
###04.07 泛型的由来
* A:案例演示
* 泛型的由来:通过Object转型问题引入
* 早期的Object类型可以接收任意的对象类型,但是在实际的使用中,会有类型转换的问题。也就存在这隐患,所以Java提供了泛型来解决这个安全问题。
###04.08 泛型类的概述及使用
* A:泛型类概述<T>
* 把泛型定义在类上
* B:定义格式
* public class 类名<泛型类型1,…>
* public class Tool<Q>{
....
}
* C:注意事项
* 泛型类型必须是引用类型
* D:案例演示
* 泛型类的使用
###04.09 泛型方法的概述和使用)
* A:泛型方法概述
* 把泛型定义在方法上
* B:定义格式
* public <泛型类型> 返回类型 方法名(泛型类型 变量名)
public<T> void show(T t){
System.out.println(t);
}
public static<W> void print(W w){
System.out.prinln(w);
}
* C:案例演示
* 泛型方法的使用
//非静态方法泛型最好与类的泛型一致;如果不一致,需要在方法上声明该泛型。
//静态方法必须声明自己的泛型(随着类的加载而加载);一般是跟类的泛型不一样。和类的声明相同是可以的,一个是创建对象时赋值,一个是调用静态方法时赋值,相当于两个变量
###04.10 泛型接口 的概述和使用
* A:泛型接口概述
* 把泛型定义在接口上
* B:定义格式
* public interface 接口名<泛型类型>
* C:案例演示
* 泛型接口的使用
//没有必要在实现接口的时候给自己类加泛型。所以一般随接口的泛型。
interface Inter<T> {
public void show(T t);
}
/*class Demo implements Inter<String> { //推荐用这种
@Override
public void show(String t) {
System.out.println(t);
}
}*/
class Demo<T> implements Inter<T> { //没有必要在实现接口的时候给自己类加泛型
@Override
public void show(T t) {
System.out.println(t);
}
}
###04.11 泛型高级之通配符
* A:泛型通配符<?>
* 任意类型,如果没有明确,那么就是Object以及任意的Java类了
List<?> list = new ArrayList<Integer>(); //当右边的泛型是不确定时,左边可以指定为?
* B:? extends E
* 向下限定,E及其子类
* C:? super E
* 向上限定,E及其父类
###04.12 增强for的概述和使用
* A:增强for概述
* 简化数组和Collection集合的遍历 ,底层依赖的是叠加器
* B:格式:
for(元素数据类型 变量 : 数组或者Collection集合) {
使用变量即可,该变量就是元素
}
* C:案例演示
* 数组,集合存储元素用增强for遍历
* D:好处
* 简化遍历
###04.13 ArrayList存储字符串和自定义对象并遍历增强for版
* A:案例演示
* ArrayList存储字符串并遍历增强for版
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
for(String s : list) {
System.out.println(s);
}
###04.14 三种迭代的能否删除
* 普通for循环,可以删除,但是 索引要--(list.remove(i--);)。 //不会出现越界异常,因为--马上又++了。
* 迭代器,可以删除,但是必须使用迭代器自身的remove方法,否则会出现并发修改异常
* 增强for循环不能删除
###04.15 静态导入的概述和使用 jdk5.0新特新
* A:静态导入概述 ,就是导入类中的静态方法
* B:格式:
* import static 包名….类名.方法名;
* import static java.util.Arrays.sort; //静态导入
* import static java.util.Arrays.*; //把类中的静态方法都导入
* import static java.util.Arrays.toString; //静态导入
* C:注意事项
* 方法必须是静态的,如果有多个同名的静态方法,就不知道使用的是哪个?这个时候要使用,必须加前缀。由此可见,意义不大,所以一般不用,但是要能看懂。
###04.16 可变参数 的概述和使用 jdk5.0新特新
* A:可变参数概述
* 定义方法的时候不知道该定义多少个参数 //可以接收很多参数。。
* B:格式
* 修饰符 返回值类型 方法名(数据类型… 变量名){}
* C:注意事项:
* 这里的变量其实是一个数组
* 如果一个方法有可变参数,还有多个参数,那么可变参数必须放在最后一个
print(11,22,33,44,55);
print(11,arr), //arr是一个int数组,11是赋值给x的
public static void print(int x,int ... arr){
for (int i : arr) {
System.out.print(i+" ");
}
}
###04.17 Arrays工具类的asList()方法的使用
* A:案例演示 ,数组转集合
* Arrays工具类的asList()方法的使用
* Collection中toArray(T[] a)泛型版的集合转数组
//数组转换成集合虽然不能增加或减少元素,但是可以利用集合的思想操作数组,可以使用集合中的其他方法。
//基本数据类型的数组是把整个数组当成一个元素存入集合。所以数组转换成集合,数组必须是引用数据类型。
public class Demo4_AsList {
public static void main(String[] args) {
//demo1();
//demo2();
//集合转数组,加泛型的
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
list.add("d");
//String[] arr = list.toArray(new String[1]);
//Integer[] arr = al.toArray(new Integer[10]);
//Object[] arr = al.toArray();
String[] arr = list.toArray(new String[10]);
//当集合转换数组时,数组长度如果是小于等于集合的size时,转换后的数组长度等于集合的size
//如果数组的长度大于了size,分配的数组长度就和你指定的长度一样
for (String string : arr) {
System.out.println(string);
}
}
public static void demo2() {
//int[] arr = {11,22,33,44,55};
//List<int[]> list = Arrays.asList(arr); //基本数据类型的数组转换成集合,会将整个数组当作一个对象转换
//System.out.println(list);
Integer[] arr = {11,22,33,44,55}; //将数组转换成集合,数组必须是引用数据类型
List<Integer> list = Arrays.asList(arr);
System.out.println(list);
}
public static void demo1() {
String[] arr = {"a","b","c"};
List<String> list = Arrays.asList(arr); //将数组转换成集合,是List集合接收
//list.add("d"); //不能添加元素
System.out.println(list);
}
}
###04.18 ArrayList嵌套ArrayList)
* A:案例演示
* 集合嵌套之ArrayList嵌套ArrayList //二维集合。。
public static void main(String[] args) {
ArrayList<ArrayList<Person>> list = new ArrayList<>();
ArrayList<Person> first = new ArrayList<>(); //创建第一个班级
first.add(new Person("杨幂", 30));
first.add(new Person("李冰冰", 33));
first.add(new Person("范冰冰", 20));
ArrayList<Person> second = new ArrayList<>();
second.add(new Person("黄晓明", 31));
second.add(new Person("赵薇", 33));
second.add(new Person("陈坤", 32));
//将班级添加到学科集合中
list.add(first);
list.add(second);
//遍历学科集合
for(ArrayList<Person> a : list) {
for(Person p : a) {
System.out.println(p);
}
}
}
###04.19 HashSet存储自定义对象保证元素唯一性
* A:案例演示
* 存储自定义对象,并保证元素唯一性。
HashSet<Person> hs = new HashSet<>();
hs.add(new Person("张三", 23));
hs.add(new Person("张三", 23));
hs.add(new Person("李四", 23));
hs.add(new Person("李四", 23));
hs.add(new Person("王五", 23));
hs.add(new Person("赵六", 23));
* 重写hashCode()和equals()方法
* 1.HashSet原理
* 我们使用Set集合都是需要去掉重复元素的, 如果在存储的时候逐个equals()比较, 效率较低,哈希算法提高了去重复的效率, 降低了使用equals()方法的次数
* 当HashSet调用add()方法存储对象的时候, 先调用对象的hashCode()方法得到一个哈希值, 然后在集合中查找是否有哈希值相同的对象
* 如果没有哈希值相同的对象就直接存入集合
* 如果有哈希值相同的对象, 就和哈希值相同的对象逐个进行equals()比较,比较结果为false就存入, true则不存
* 2.将自定义类的对象存入HashSet去重复
* 类中必须重写hashCode()和equals()方法
* hashCode(): 属性相同的对象返回值必须相同, 属性不同的返回值尽量不同(提高效率)
* equals(): 属性相同返回true, 属性不同返回false,返回false的时候存储
public boolean equals(Object obj) {
if (this == obj)
return true; //调用的对象与传入的对象是同一个对象,返回true
if (obj == null)
return false; //传入的对象为null,返回false (调用的对象不可能是null,否则会出现异常)
if (getClass() != obj.getClass())
return false; //判断两个字节码文件是否是同一个,不是直接返回false
Person other = (Person) obj; // 是,转换类型,这样不会出现类型转换异常
if (age != other.age)
return false; //调用对象的年龄不等于传入对象的年龄
if (name == null) { //调用对象的年龄为null,(这是对象的属性name为null 不是对象为null)
if (other.name != null)
return false;
} else if (!name.equals(other.name)) //调用对象的姓名不等于传入对象的姓名
return false;
return true;
}
###04.20 TreeSet原理
* 1.特点
* TreeSet是用来排序的, 可以指定一个顺序, 对象存入之后会按照指定的顺序排列
* 2.使用方式
* a.自然顺序(Comparable)
* TreeSet类的add()方法中会把存入的对象提升为Comparable类型
* 调用对象的compareTo()方法和集合中的对象比较
* 根据compareTo()方法返回的结果进行存储
* b.比较器顺序(Comparator)
* 创建TreeSet的时候可以制定 一个Comparator
* 如果传入了Comparator接口的子类对象, 那么TreeSet就会按照比较器中的顺序排序
* add()方法内部会自动调用Comparator接口中compare()方法排序
* 调用的对象是compare方法的第一个参数,集合中的对象是compare方法的第二个参数
* c.两种方式的区别
* TreeSet构造函数什么都不传, 默认按照类中Comparable的顺序(没有就报错ClassCastException)
* TreeSet如果传入Comparator, 就优先按照Comparator
###04.21 键盘录入学生信息按照总分排序后输出在控制台
* 需求:键盘录入5个学生信息(姓名,语文成绩,数学成绩,英语成绩),按照总分从高到低输出到控制台。
Scanner sc = new Scanner(System.in);
System.out.println("请输入5个学生成绩格式是:(姓名,语文成绩,数学成绩,英语成绩)");
TreeSet<Student> ts = new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s2.getSum() - s1.getSum(); //根据学生的总成绩降序排列
return num == 0 ? 1 : num;
}
});
while(ts.size() < 5) {
String line = sc.nextLine();
try {
String[] arr = line.split(",");
int chinese = Integer.parseInt(arr[1]); //转换语文成绩
int math = Integer.parseInt(arr[2]); //转换数学成绩
int english = Integer.parseInt(arr[3]); //转换英语成绩
ts.add(new Student(arr[0], chinese, math, english));
} catch (Exception e) {
System.out.println("录入格式有误,输入5个学生成绩格式是:(姓名,语文成绩,数学成绩,英语成绩");
}
}
System.out.println("排序后的学生成绩是:");
for (Student s : ts) {
System.out.println(s);
}
public class Student {
private String name;
private int chinese;
private int math;
private int english;
private int sum; //
public Student() {
super();
}
public Student(String name, int chinese, int math, int english) {
super();
this.name = name;
this.chinese = chinese;
this.math = math;
this.english = english;
this.sum =this.chinese + this.math + this.english; //
}
public int getSum() { //
return sum;
}
@Override
public String toString() {
return "姓名"+name+",语文"+chinese+",数学"+math+",英语"+english+",总成绩"+sum;
}
}
###04.22 Map集合概述和特点
* A:Map接口概述
* 查看API可以知道:
* 将键映射到值的对象
* 一个映射不能包含重复的键
* 每个键最多只能映射到一个值
* B:Map接口和Collection接口的不同
* Map是双列的,Collection是单列的
* Map的键唯一,Collection的子体系Set是唯一的
* Map集合的数据结构值针对键有效,跟值无关;Collection集合的数据结构是针对元素有效
###04.23 HashMap和Hashtable的区别
* A:面试题
* HashMap和Hashtable的区别
* Hashtable是JDK1.0版本出现的,是线程安全的,效率低,HashMap是JDK1.2版本出现的,是线程不安全的,效率高
* Hashtable不可以存储null键和null值;HashMap可以存储null键和null值,使后面的代码能继续运行下去
* Hashtable是类命名没有按约定的规则
* B:案例演示
* HashMap和Hashtable的区别
* Hashtable<Integer,String> ht = new Hashtable<>();
ht.put(null, "国民老公");
ht.put(33, "null");