1.集合体系结构(分为单列和双列)
单列集合是指添加数据时只能添加一个,双列集合是每次添加一对集合。
2.单列集合(collections)
(1)List系列:添加的元素是有序,可重复,有索引
Set系列:添加的元素是无序,不重复,无索引
(2)常用方法
contain()方法对于自定义对象需要进行重写,底层依赖equals()方法,比较的是地址的值,但是我们需要的是对象属性相等即为contain。
(3)collections遍历
三种遍历方式,不包括for循环(依赖索引),set没有索引不能用for循环。
a.迭代器遍历
用于遍历输出集合元素
Interator<String>it = list.iterator();//用于指向第一个索引位置
细节:
1.报的是没有这个元素异常而不是索引越界,迭代器不依赖索引
2.在迭代器遍历时,不能用list.move()会报错,只能用it.remove();但是没有添加的替换方法。
b.增强for遍历
只有单列集合和数组才能用增强for遍历
for(数据类型 变量名: 集合){sYstem.out.println(变量名);}
c.Lambda表达式遍历()->{}
集合名.forEach(对象名 -> System.out.println(对象名));
追源码其实就是使用了一个for循环,然后使用匿名内部类 集合名.forEach(new Consumer<String>(){ @override public void accept(String s){ System.out.println(s); } });
3.List接口
List实现了collections接口,方法也都实现了。list集合有索引,所以有很多索引操作的方法。
List<String> list = new ArrayList<>();//使用了泛型,List是接口不能直接创建对象 list.add("aaa");list.add("bbb");list.add("ccc"); list.add(0,"qqq");//修改元素 list.remove()//可以根据传入索引删除,还可以直接传入值 //如果方法出现重载现象,就先调用实参和形参类型一致的方法,而不是需要手动装箱再拆箱 list.add(1);//第四个元素,如果我要删除这个元素,不通过索引值,可以通过手动装箱的方式 Integer i = Integer.valueOf(1); list.remove(i); //修改元素 String str = list.set("fff");//返回修改的值,qqq; //获取元素 String str = list.set(0);//返回获取的值,fff;
(1)List集合的遍历方式
迭代器遍历、列表迭代器遍历、增强for循环、Lambda表达式遍历、普通for循环遍历
Interator<String>it = list.iterator(); while(it.hasNext()){ String s = it.next(); System.out.println(s); }
列表迭代器(add,remove,hasNext,next)
4.数据结构(栈、队列、数组、链表、二叉树、二叉查找树、平衡二叉树、红黑树)
什么时候用哪种集合???怎么结合具体的业务场景来进行选择??
1.每种数据结构长什么样子?2.如何添加数据?3.如何删除数据?
(1)栈(先进后出)
数据进栈称为进栈和压栈,离开栈模型的过程称为弹栈和出栈。
(2)队列(先进先出)
(3)数组
查询快,增删比较慢。
(4)链表
链表中的结点时独立的对象,在内存中是不连续的,每个结点包含数据值和下一个结点的地址。
查询慢,增删比较快。
a.单向链表、双向链表
5.ArrayList源码、Linklist源码、迭代器源码(面试题!后续补)
6.泛型
泛型中只能支持引用数据类型,不能写基本数据类型,只在编译时判断,是否是统一数据类型,在运行时又转为obj类型
(1)泛型类、泛型接口、泛型方法
不知道存什么数据类型(测试类)
MyArrayList<String> list = new MyArrayList<>(); list.add("aaa"); list.add("bbb"); list.add("ccc"); System.out.println(list); MyArrayList<String> list1 = new MyArrayList<>(); list1.add(1); list1.add(3); list1.add(5); //System.out.println(list1.get(0))报错会转换成Integer类型 int i = list1.get(0); System.out.println(i);
a.泛型类
public class MyArrayList<E>{ Object[] obj = new Object[10]; int size; public Boolean add(E e){ obj[size] = e; size++; return true; } public E get(int index){ return (E)obj[index];//返回时要强转,因为在运行时会转成obj类型所以取出的时候要强转 } public String toString(){ return Arrays.toString(obj); } }
b.泛型方法
当方法中形参类型不确定时,可以定义在方法后面
格式:
public <类型>返回值类型 方法名 (类型 变量名) public <T>void show(T t){}
public class ListUtil{/* Object[] obj = new Object[10]; int size; public Boolean add(E e){ obj[size] = e; size++; return true; } */ public void static<T> addAll(ArrayList<T> list,T t2, T t3){ list.add(t2); list.add(t3); }//添加多个元素public void static<T> addA(ArrayList<T> list,T...t){for(T element : list){list.add(element);} } public String toString(){ return Arrays.toString(obj); } }
//测试类 //泛型的类型在方法被调用时确定下来的 ArrayList<String> list = new ArrayList<>(); ListUtil.addAll(list,"aaa","BBB"); System.out.println(list);//aaa,BBB ArrayList<Integer> list2 = new ArrayList<>(); ListUtil.addA(list2,1,2,3,4,5,5,6); System.out.println(list2);
c.泛型接口
import java.util.list; public class MyArrayList implements list<String>{ //实现所有方法 } public class MyArrayList1 implements list<E>{ //实现所有方法 }
//测试类 MyArrayList list = new MyArrayList(); //只能添加String类型 list.add("qqq"); //实现类不确定类型,可以在创建对象时确定类型 MyArrayList1<String> list1 = new MyArrayList1<>();
(2)泛型的通配符和综合类型
泛型没有继承性,但是数据具备继承性
public void static<People> addA(ArrayList<People> list){}//只能传入people//数据具有继承性MyArrayList1<People> list3 = new MyArrayList1<>(); list3.add(new Teacher()); list3.add(new Student());
通配符?用于限制传入数据类型的范围
public void static<T> addAll(ArrayList<? extends Student,Teacher,String> list){ list.add(t2); list.add(t3); } //测试类main MyArrayList list = new MyArrayList(); //只能添加String类型 list.add("qqq"); //实现类不确定类型,可以在创建对象时确定类型 MyArrayList1<String> list1 = new MyArrayList1<>(); MyArrayList1<Student> list2 = new MyArrayList1<>(); MyArrayList1<Teacher> list3 = new MyArrayList1<>(); listUtil.addAll(list1); listUtil.addAll(list2); listUtil.addAll(list3); class Student{} class Teacher{}
7.set系列
(1)HashSet
不重复体现在hashcode和equals方法,一般地通过new一个对象,地址值是不同的,所以哈希值也会不同,引用类型一般重写了两种方法。但是自定义类型需要重写。
a.HashSet的底层原理
package harper1125.Set; import java.util.HashSet; import java.util.Iterator; import java.util.function.Consumer; /*** 特点:无序,不重复,无索引*/ public class HashSet_ {public static void main(String[] args) {HashSet<Integer> it = new HashSet<>();it.add(3);it.add(2);it.add(9);it.add(8);//三种遍历方式//迭代器Iterator<Integer> its = it.iterator();while(its.hasNext()){int i = its.next();System.out.println(i);}System.out.println("===========");//增强for循环for (int j:it) {System.out.println(j);}System.out.println("----------------------");System.out.println(it);//[2, 3, 8, 9] } }
b.三个问题
HashSet根据哈希值计算存入的位置,但是可能哈希值相等的元素,而且jdk8之后是存入的元素挂在老元素后面。索引的话可能几个不同的元素有相同的索引,增加了麻烦,以及HashSet通过equals方法和hashcode比较去重。
(2)LinkedHashSet
(3)TreeSet
最重要的是可排序,以及实现compareTo接口
package harper1125.Set; import java.util.TreeSet; /*** 不重复,无索引,可排序* 可排序是按照从小到大的顺序,参照ASCII表* 底层基于红黑树数据结构,增删改查性能好**/ public class TreeSet_ {public static void main(String[] args) {TreeSet<Integer> ts = new TreeSet();ts.add(2);ts.add(1);ts.add(-9);ts.add(3);ts.add(8);System.out.println(ts);TreeSet<Student> s = new TreeSet();s.add(new Student("harper",19));s.add(new Student("jack",20));s.add(new Student("jill",76));s.add(new Student("Alice",5));System.out.println(s); } } class Student implements Comparable<Student>{String name;int age; 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;} @Overridepublic int compareTo(Student o) {System.out.println(this);int ages = this.age - o.getAge();if(ages != 0) return ages;return this.name.compareTo(o.getName());} @Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';} }