文章目录
【一】集合的引入
【1】数组,集合都是对多个数据进行存储操作的,简称为容器。
PS:这里的存储指的是内存层面的存储,而不是持久化存储。
【二】数组:
【1】特点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
int [ ] arr=new int [ 6 ];
(2)数组一旦声明了类型以后,数组中只能存放这个类型的数据。数组中只能存放同一种类型的数据。
int [ ] arr ,String [ ] , double [ ] ,·····
【2】缺点:
(1)数组一旦指定了长度,那么长度就被确定了,不可以更改。
(2)删除,增加元素,效率低。
(3)数组中实际元素的数量是没有办法获取的,没有提供对应的方法或者属性来获取。
(4)数组存储:有序,可重复。对于无序,不可重复的数组不能满足要求。
【3】集合
正因为上面的缺点,引入了一个新的存储数据的结构—》集合
【4】为什么要引入集合?
集合一章我们会学习很多集合,为什么要学习这么多集合?
因为 不同集合底层数据结构不一样。
【三】简要集合结构图
【四】集合应用场合
前端后端数据库交互:
当需要将相同结构的个体整合到一起的时候,需要集合。
实际应用场合:
【五】Collection接口
【1】Collection常用方法
public class Test01 {
public static void main(String[] args) {
/*
collection接口的常用方法:
增加:add(E e) addAll(Collection<? extends E> c)
删除:clear() remove(Object o)
修改:
查看:iterator() size()
判断:contains(Object o) equals(Object o) isEmpty()
*/
//创建对象:接口不能创建对象,利用实现类创建对象
Collection col=new ArrayList();
//调用方法:
//集合有一个特点:只能存放引用数据类型的数据,不能是基本数据类型
//基本数据类型自动装箱,对应包装类,int--->integer
col.add(18);
col.add(12);
col.add(11);
col.add(17);
System.out.println(col/*.toString()*/);
List list = Arrays.asList(new Integer[]{11, 15, 17, 9});
col.addAll(list);//将另一个集合添加入col中
System.out.println(col);
//col.clear();//清空集合
System.out.println(col);
System.out.println("集合中元素的数量为:"+col.size());
System.out.println("集合是否为空:"+col.isEmpty());
boolean isRemove = col.remove(15);
System.out.println(col);
System.out.println("集合中数据是否被删除"+isRemove);
Collection col2=new ArrayList();
col2.add(18);
col2.add(12);
col2.add(11);
col2.add(17);
Collection col3=new ArrayList();
col3.add(18);
col3.add(12);
col3.add(11);
col3.add(17);
System.out.println(col2.equals(col3));//比较元素的值是否相等
System.out.println(col2 == col3);//比较地址
System.out.println(col.contains(17));
System.out.println(col3.contains(112));
}
}
【2】Collecation集合的遍历
迭代器简要原理图:
public class Test02 {
public static void main(String[] args) {
Collection col=new ArrayList();
col.add(18);
col.add(12);
col.add(11);
col.add(17);
//对集合遍历(对集合中元素进行查看)
//方式一:普通for循环 失败
// for(int i=0;i<col.size();i++){
// col.
// }
//方式二 :增强for循环
for(Object o:col){
System.out.println(o);
}
//方式三:
Iterator it = col.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
【六】Collection_List接口
public class Test03 {
public static void main(String[] args) {
/*
List接口中常见的方法:
增加:add(int index, E element)
修改:set(int index, E element)
删除:remove(int index) remove(Object o)
查找:get(int index)
判断:
*/
List list=new ArrayList();
list.add(13);
list.add(17);
list.add(6);
list.add(-1);
list.add("abc");
System.out.println(list);
list.add(3,66);
System.out.println(list);
list.set(3,77);
System.out.println(list);
list.remove(2);//在集合中存入的是Integer类型数据的时候,调用remove方法调用的是:remove(int index)
System.out.println(list);
list.remove("abc");
System.out.println(list);
Object o = list.get(0);
System.out.println(o);
//List集合 遍历:
//方式一:普通for循环
for(int i=0;i<list.size();i++){
System.out.println(list.get(i));
}
//方式二:增强for循环
for(Object obj:list){
System.out.println(obj);
}
//方式三:迭代器
Iterator it= list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
}
}
【七】ArrayList实现类
【1】JDK1.7源码:
【1】重要属性:
在JDK.7中,在调用构造器的时候给底层数组elementData初始化,数组初始化长度为10
【2】内存分析:
【3】调用add方法:
当数组中的10个位置都满了的时候就开始进行数组的扩容,扩容长度为原数组的1.5倍;
【2】JDK1.8源码:
【1】JDK1.8底层依旧是Object类型的数组,size数组中有效长度:
【2】ArrayList a1=new ArrayList( );
【3】add方法:
【八】Vector实现类
【1】底层Object数组,int类型属性表示数组中有效长度:
【2】Vector v=new Vector( )调用构造器
【3】add方法:
【九】泛型的引入
【1】什么是泛型?(Generic)
泛型相当于标签
形式:< >
集合容器
【2】没有泛型的时候使用集合:
public class Test01 {
public static void main(String[] args) {
//创建一个ArrayList集合,向这个集合中存入学生的成绩:
ArrayList al=new ArrayList();
al.add(89);
al.add(46);
al.add(59);
al.add(83);
al.add("张三");
//对集合遍历查看:
for(Object obj:al){
System.out.println(obj);
}
}
}
如果不适用泛型的时候,有缺点:
一般我们在使用的时候基本上往集合中存入的都是相同类型的数据便于管理,所以现在什么引用数据类型都可以存入集合,不方便管理!
【3】JDK1.5以后使用泛型,集合中使用泛型:
public class Test01 {
public static void main(String[] args) {
//创建一个ArrayList集合,向这个集合中存入学生的成绩:
//加入泛型的优点:在编译时期就会对类型进行检查,不是泛型对应的类型就不可以添加这个集合。
ArrayList<Integer> al=new ArrayList<Integer>();
al.add(89);
al.add(46);
al.add(59);
al.add(83);
// al.add("张三");
//对集合遍历查看:
/*for(Object obj:al){
System.out.println(obj);
}*/
for(Integer i:al)
{
System.out.println(i);
}
}
}
【4】泛型总结:
(1)JDK1.5以后。
(2)泛型实际就是一个**< >**引起来的参数类型,这个参数类型具体在使用的时候才会确定具体的类型。
(3)使用了泛型以后,可以确定集合中存放数据的类型,在编译时期就可以检查。
(4)使用泛型你可能觉的麻烦,实际使用了泛型才会简单,后续的遍历操作简单
(5)泛型的类型:都是引用数据类型,不能是基本数据类型。
(6)
ArrayList<Integer> al=new ArrayList<Integer>();在JDK1.7以后可以写为:
ArrayList<Integer> al=new ArrayList<>(); ----<>----砖石运算符
【十】自定义泛型结构_泛型类_泛型接口
【1】泛型的引入
public class GenericTest<E> {
int age;
String name;
E sex;
public void a(E n){
}
public void b(E[] m){
}
}
class Test{
public static void main(String[] args) {
//GenericTest进行实例化:
//(1)实例化的时候不指定泛型:如果实例化的时候不明确的指定类的泛型,那么认为此类型为Object类型
GenericTest gt1=new GenericTest();
gt1.a("abc");
gt1.a(17);
gt1.a(9.8);
gt1.b(new String []{"a","b","c"});
//(2)实例化的时候指定泛型:--->推荐
GenericTest<String > gt2=new GenericTest();
gt2.sex="男";
gt2.a("男");
gt2.b(new String []{"a","b","c"});
}
}
【2】继承情况:
(1)父类指定泛型:
class SubGenericTest extends GenericTest<Integer>{
}
class Demo{
public static void main(String[] args) {
//指定父类泛型,那么子类就不需要再指定泛型了,可以直接使用。
SubGenericTest sgt=new SubGenericTest();
sgt.a(19);
}
}
(2)父类不指定泛型:
如果父类不指定泛型,那么子类也会变成一个泛型类,那这个E的类型可以在创建子类对象的时候确定:
class SubGenericTest2<E> extends GenericTest<E>{
}
class Demo2{
public static void main(String[] args) {
SubGenericTest2<String> s=new SubGenericTest2<>();
s.a("abc");
s.sex="女";
}
}
【3】应用场合:
【4】细节:
【1】泛型类可以定义多个参数类型。
【2】泛型类的构造器写法:
【3】不同的泛型的引用类型不可以相互赋值:
【4】泛型如果不指定,那么就会被擦除,泛型对应的类型为Object类型。
【5】泛型类中的静态方法不能使用类的泛型。
【6】不能直接使用数组 E[ ] 的创建
【十一】泛型方法
/*
* 1.什么是泛型方法?
* 不是带泛型的方法就是泛型方法
* 泛型方法有要求:这个方法的泛型的参数类型要和当前的类的泛型无关
*
* 泛型方法对应的那个泛型参数类型 和 当前所在的这个类 是否是泛型类,泛型是啥 无关
* 2.泛型方法定义的时候,前面要加上<T>
* 原因:如果不加的话,会把T当做一种数据类型,然而代码中没有T类型那么就会报错
* 3.T的类型是在调用方法的时候确定的
* 4.泛型方法是否是静态方法?
* */
public class TestGeneric<E> {
//不是泛型方法(不能是静态方法)
public void a(E e){
}
//是泛型方法
public static <T> void b(T t){
}
}
class Demo{
public static void main(String[] args) {
TestGeneric<String > tg=new TestGeneric<>();
tg.a("abc");
tg.b("abc");
tg.b(15);
tg.b(true);
}
}
【十二】自定义泛型结构_泛型参数存在继承关系的情况
public class test05 {
public static void main(String[] args) {
Object obj=new Object();
String s = new String();
obj=s;//多态的一种形式
Object []objArr=new Object[10];
String []strArr=new String[10];
objArr=strArr;//多态的一种形式
List<Object> list1=new ArrayList<>();
List<String> list2=new ArrayList<>();
list1=list2; //报错
//总结:A和B是子类父类的关系,但是G<A>和G<B>不存在继承关系,是并列关系
}
}
【十三】自定义泛型结构_通配符
【1】在没有通配符的时候:
下面的a方法,相当于方法的重复定义,报错
public class Test {
public void a(List<Object> list){
//报错
}
public void a(List<String> list){
}
public void a(List<Integer> list){
}
}
【2】引入通配符:
public class Demo {
public static void main(String[] args) {
List<Object> list1=new ArrayList<>();
List<String> list2=new ArrayList<>();
List<Integer> list3=new ArrayList<>();
List<?>list=null;
list=list1;
list=list2;
list=list3;
}
}
发现:A 和 B 是子类父类的关系,G<A>和G<B>不存在子类父类关系,是并列的
加入通配符?后,G<?>就变成了G<A>和G<B>的父类
【3】使用通配符:
public class Test {
/*public void a(List<Object> List){
//报错
}
public void a(List<String> List){
}
public void a(List<Integer> List){
}*/
public void a(List<?> list) {
//内部遍历的时候用Object即可,不用?
for (Object o : list) {
System.out.println(o);
}
}
}
class T{
public static void main(String[] args) {
Test t=new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}
【4】查看API中应用位置:
【5】细节:
【1】.遍历
【2】.数据的写入操作
【3】.数据的读取操作
public class Test {
/*public void a(List<Object> List){
//报错
}
public void a(List<String> List){
}
public void a(List<Integer> List){
}*/
public void a(List<?> list) {
//1.遍历
for (Object o : list) {
System.out.println(o);
}
//2.数据的写入操作
//list.add("abc"); 报错--》不能随意的添加数据
list.add(null);
//3.数据的读取操作
Object s=list.get(0);
}
}
class T{
public static void main(String[] args) {
Test t=new Test();
t.a(new ArrayList<Integer>());
t.a(new ArrayList<String>());
t.a(new ArrayList<Object>());
}
}
【十四】自定义泛型结构_泛型受限
【1】 泛型的上限:
【2】泛型的下限:
public class Test {
public static void main(String[] args) {
//a,b,c三个集合是并列关系:
List<Object> a=new ArrayList<>();
List<Person> b=new ArrayList<>();
List<Student> c=new ArrayList<>();
/*开始使用泛型受限: 泛型的上限:
* List<? extends Person>
* 就相当于:
* List<? extends Person>是List<Person>的父类,是List<Person的子类>的父类
* */
List<? extends Person> list1=null;
/*list1=a;//报错
list1=b;
list1=c;*/
/*开始使用泛型受限: 泛型的下限:
* List<? super Person>
* 就相当于:
* List<? super Person>是List<Person>的父类,是List<Person的父类>的父类
* */
List<? super Person>list2=null;
/*list2=a;
list2=b;
list2=c;//报错
*/
}
}
【十五】LinkedList实现类的使用
【1】常用的方法、遍历的三种方式:
public class Test {
public static void main(String[] args) {
/*
LinkedList常用方法:
增加 addFirst(E e) addLast(E e) offer(E e) offerFirst(E e) offerLast(E e)
删除 poll() pollFirst()--》JDK1.6以后新出的方法,提高了代码的健壮性
pollLast() remove() remove(int index) removeFirst()
修改
查看 element() getFirst() getLast() indexOf(Object o) lastIndexOf(Object o) peek()
peekFirst() peekLast()
判断
*/
//创建一个LinkedLsit对象
LinkedList<String> list = new LinkedList<>();
list.add("aaaaaaaa");
list.add("bbbbbbbbb");
list.add("ccccccccccc");
list.add("eeeeeeeeeee");
list.add("ffffffffffff");
list.addFirst("jjjjj");
list.addFirst("hhhh");
list.offer("kk");//添加元素在尾端
list.offerFirst("pp");
list.offerLast("rr");
System.out.println(list.poll());//删除头上的元素,并且将元素输出
System.out.println(list.pollFirst());//删除头上的元素,并且将元素输出
System.out.println(list.pollLast());//删除尾上的元素,并且将元素输出
System.out.println(list.removeFirst());
System.out.println(list.removeLast());
System.out.println(list);//LinkedList可以添加重复数据
//list.clear();//清空链表
/*System.out.println(list);
*//*System.out.println(list.pollFirst());*//*
*//*System.out.println(list.removeFirst());*//*// 报错: Exception in thread "main" java.util.NoSuchElementException*/
//集合的遍历:
System.out.println("-----------------");
//普通for循环;
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//增强for:
for(String s:list){
System.out.println(s);
}
/*//迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
System.out.println(it.next());
}*/
//下面这中方式好,节省内存
for(Iterator<String> it = list.iterator();it.hasNext();){
System.out.println(it.next());
}
}
}
【2】LinkedList简要底层结构原理图
学过c语言数据结构的跳过
【3】模拟LinkedList源码:
【1】模拟链表代码
public class Node {//结点类
//三个属性:
//上一个元素的地址:
private Node pre;
//当前存入的元素:
private Object obj;
//下一个元素地址:
private Node next;
public Node getPre() {
return pre;
}
public void setPre(Node pre) {
this.pre = pre;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"pre=" + pre +
", obj=" + obj +
", next=" + next +
'}';
}
}
public class MyLinkedLst {
//链中一定有一个首结点
Node first;
//链中一定有一个尾结点
Node last;
//计数器
int count;
//提供一个构造器:
public MyLinkedLst() {
}
//添加元素的方法:
public void add(Object o){
if(first==null){//证明你添加的元素是第一个结点:
//将添加的元素封装为一个Node对象:
Node n=new Node();
n.setPre(null);
n.setObj(o);
n.setNext(null);
//当前链中第一个结点变为n
first=n;
//当前链中最后一个结点也变为n
last=n;
}else{//证明已经不是第一个结点了
//将添加的元素封装为一个对象
Node n = new Node();
n.setPre(last);//n的上一个结点一定是当前链中的最后一个结点last
n.setObj(o);
n.setNext(null);
//当前链中的最后一个结点的下一个元素 要指向n
last.setNext(n);
//将最后一个结点变为n
last=n;
}
//链中元素数量加1
count++;
}
//得到集合中元素的数量:
public int gerSize(){
return count;
}
//通过下边得到元素:
public Object get(int index){
//获取链表的头元素
Node n=first;
//一路next的到想要的元素、
for(int i=0;i<index;i++){
n=n.getNext();
}
return n.getObj();
}
}
class Test{
public static void main(String[] args) {
//创建一个MyLinkedList集合对象:
MyLinkedLst ml = new MyLinkedLst();
ml.add("aa");
ml.add("bb");
ml.add("cc");
System.out.println(ml.count);
System.out.println(ml.get(2));
}
}
【2】Debug验证数据添加成功:
【4】LinkedList源码解析(JDK1.8)
【1】JDK1.7和JDK1.8的LinkedList的源码是一致的。
【2】代码解析 LinkedList
public class LinkedList<E>{//E是一个泛型,具体的类型要在实例化的时候才会最终确定
transient int size = 0;//集合中元素的数量。
private static class Node<E> {
E item;//当前元素
Node<E> next;//指向下一个元素地址
Node<E> prev;//上一个元素地址
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
transient Node<E> first;//链表的首结点
transient Node<E> last;//链表的尾结点
//定义空构造器
public LinkedList() {
}
public boolean add(E e) {
linkLast(e);
return true;
}
//添加元素操作
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) { //添加的元素e
final Node<E> l = last; //将链表中的Last结点给1,如果是第一个元素的话 l为null
//将元素封装为一个Node 具体的对象。
final Node<E> newNode = new Node<>(l, e, null);
//将链表的last结点指向新的创建的对象:
last = newNode;
if (l == null) //如果添加的是第一个结点
first = newNode; //将链表的frist结点指向新结点
else //如果添加的不是第一个结点
l.next = newNode; //将l的下一个指向新的结点
size++; //集合中元素数量加1操作:
modCount++;
}
//获取集合中元素的数量
public int size() {
return size;
}
//通过索引得到元素:
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int index) {
// assert isElementIndex(index);
//如果index在链表的前半段,那么从前往后找
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else { //如果index在链表的后半段,那么从后往前找
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
}
【5】迭代器
【1】面试题:iterator( ),Iterator,Iterable关系
【2】hasNext( ),next( )的具体的实现:
【3】增强for循环,底层也是通过迭代器实现
【4】ListIterator迭代器
(1)加入字符串:
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
//在"cc"之后添加一个字符串"kk"
Iterator<String> it = list.iterator();
while(it.hasNext()){
if("cc".equals(it.next())){
list.add("kk");
}
}
}
}
发现报错:并发修改异常
出错原因:就是迭代器和list同时对集合进行操作;
解决方法:事情让一"人"做----》引入新的迭代器: ListIterator
迭代和添加操作都是靠ListIterator来完成的:
public class Test02 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
list.add("dd");
list.add("ee");
//在"cc"之后添加一个字符串"kk"
ListIterator<String> it = list.listIterator();
while(it.hasNext()){
if("cc".equals(it.next())){
it.add("kk");
}
}
System.out.println(it.hasNext());
System.out.println(it.hasPrevious());
//逆向遍历:
while(it.hasPrevious()){
System.out.println(it.previous());
}
System.out.println(list);
}
}
【十六】set接口
没有跟索引相关的方法。
遍历: 迭代器 增强for循环
【十七】HashSet实现类的使用
【1】放入Integer类型的数据
public class TestInteger {
public static void main(String[] args) {
//创建一个HashSet集合:
HashSet<Integer> hs=new HashSet<>();
hs.add(19);
hs.add(5);
hs.add(20);
hs.add(19);
hs.add(41);
hs.add(0);
System.out.println(hs.size());//唯一并且无序
System.out.println(hs);
}
}
【2】放入String类型数据
public class TestString {
public static void main(String[] args) {
HashSet<String> hs = new HashSet<>();
System.out.println(hs.add("hello")); //true
hs.add("apple");
hs.add("banana");
hs.add("css");
System.out.println(hs.add("apple"));//false 没有放入到集合中
hs.add("html");
System.out.println(hs.size());
System.out.println(hs);
}
}
【3】放入自定义的引用数据类型的数据:
public class Student {
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
}
public class TestStudent {
public static void main(String[] args) {
//创建一个HashSet集合:
HashSet<Student> hs = new HashSet<>();
hs.add(new Student(19,"莉莉"));
hs.add(new Student(20,"菲菲"));
hs.add(new Student(18,"娜娜"));
hs.add(new Student(19,"小爱"));
hs.add(new Student(19,"莉莉"));
hs.add(new Student(19,"洋洋"));
System.out.println(hs.size());
System.out.println(hs);
}
}
上面自定义的类型不满足 唯一,无序的特点。Why?
【4】HashSet原理图:
【5】疑问:
1.数组的长度是多少?
2.数组的类型是多少 ?
3.hashcode 和 equls方法真的调用了吗?
4.表达式是什么?
5.同一个位置的数据向前放还是向后放?
6.放入数组中的数据,是直接放吗?
【6】LinkedHashSet使用:
其实就是在HashSet的基层上,多了一个总的链表,这个总链表将放入的元素串在一起,方便有序的遍历。
代码:
public class TestInteger {
public static void main(String[] args) {
//创建一个HashSet集合:
LinkedHashSet<Integer> hs=new LinkedHashSet<>();
hs.add(19);
hs.add(5);
hs.add(20);
hs.add(19);
hs.add(41);
hs.add(0);
System.out.println(hs.size());//唯一并且无序
System.out.println(hs);
}
}
【7】 比较器的使用:
【1】以int类型为例
比较的思路:将比较的数据做差,返回一个int类型的数,将这个int类型的数值 按照= 0 >0 <0
public class test08 {
public static void main(String[] args) {
int a=10;
int b=20;
System.out.println(a-b);//=0 <0 >0
}
}
【2】比较String类型的数据:
String 类实现了Compare接口,这个接口中有一个抽象方法compareTo,String类中重写这个方法即可。
public static void main(String[] args) {
/* int a=10;
int b=20;
System.out.println(a-b);//=0 <0 >0*/
String a="A";
String b="B";
System.out.println(a.compareTo(b));
}
【3】比较double类型的数据:
double a=9.6;
double b=8.3;
System.out.println(((Double) a).compareTo((Double) b));
【4】比较自定义的数据类型:
(1)内部比较器:
public class Student implements Comparable<Student>{
@Override
public int compareTo(Student o) {
//按照年龄进行比较:
//return this.getAge()-o.getAge();
//按照身高进行比较:
//return ((Double)(this.getHeight())).compareTo((Double)(o.getHeight()));
//按照名字进行比较
return this.getName().compareTo(o.getName());
}
private int age;
private double height;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, double height, String name) {
this.age = age;
this.height = height;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", height=" + height +
", name='" + name + '\'' +
'}';
}
}
public class Test02 {
public static void main(String[] args) {
//比较两个学生:
Student s1 = new Student(10,58,"宝宝");
Student s2 = new Student(14,69,"大宝贝");
System.out.println(s1.compareTo(s2));
}
}
(2)外部比较器:
public class Student{
private int age;
private double height;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, double height, String name) {
this.age = age;
this.height = height;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", height=" + height +
", name='" + name + '\'' +
'}';
}
}
class BiJiao1 implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
//比较年龄
return o1.getAge()-o2.getAge();
}
}
class BiJiao2 implements Comparator<Student>{
@Override
//比较姓名
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
class BiJiao3 implements Comparator<Student>{
@Override
//在年龄相同的情况下比较身高 年龄不同比较年龄
public int compare(Student o1, Student o2) {
if ((o1.getAge() - o2.getAge()) == 0) {
return ((Double) (o1.getHeight())).compareTo((Double) (o2.getHeight()));
} else {
//年龄不一样
return o1.getAge()-o2.getAge();
}
}
}
public class Test01 {
public static void main(String[] args) {
//比较两个学生:
Student s1 = new Student(10,58,"宝宝");
Student s2 = new Student(14,69,"大宝贝");
//获取外部比较器:
Comparator bj1 = new BiJiao1();
System.out.println(bj1.compare(s1, s2));
Comparator bj2=new BiJiao2();
System.out.println(bj2.compare(s1,s2));
Comparator bj3=new BiJiao2();
System.out.println(bj3.compare(s1,s2));
}
}
【5】外部比较器和内部比较器 谁好呐?
答案: 外部比较器,多态,扩展性好
【十八】TreeSet的使用及原理
【1】存入Integer类型数据:(底层利用的是内部比较器)
特点:唯一,无序(没有按照输入顺序进行遍历),有序(按照升序遍历)
public class Test01 {
public static void main(String[] args) {
//创建一个TreeSet:
TreeSet<Integer> ts=new TreeSet<>();
ts.add(12);
ts.add(3);
ts.add(7);
ts.add(9);
ts.add(3);
ts.add(16);
System.out.println(ts.size());
System.out.println(ts);
}
}
【2】原理:底层:二叉树( 数据结构中的一个逻辑结构)
【3】放入String类型数据:(底层也是类内部比较器)
public class Test02 {
public static void main(String[] args) {
TreeSet<String> ts=new TreeSet<>();
ts.add("e莉莉");
ts.add("b莉莉");
ts.add("d莉莉");
ts.add("a莉莉");
ts.add("g莉莉");
ts.add("f莉莉");
ts.add("c莉莉");
System.out.println(ts.size());
System.out.println(ts);
}
}
【4】想放入自定义的Student类型的数据:
(1)利用内部比较器:
public class Student implements Comparable<Student>{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
@Override
public int compareTo(Student o) {
return this.getAge()-o.getAge();
}
}
public class Test03 {
public static void main(String[] args) {
TreeSet<Student> st=new TreeSet<>() ;
st.add(new Student(12,"c莉莉"));
st.add(new Student(8,"a莉莉"));
st.add(new Student(4,"g莉莉"));
st.add(new Student(9,"b莉莉"));
st.add(new Student(10,"e莉莉"));
st.add(new Student(11,"f莉莉"));
st.add(new Student(1,"d莉莉"));
System.out.println(st.size());
System.out.println(st);
}
}
(2)通过外部比较器:
public class Student{
private int age;
private String name;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student(int age, String name) {
this.age = age;
this.name = name;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
'}';
}
}
class BiJiao implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}
public class Test03 {
public static void main(String[] args) {
//利用外部比较器,必须自己指定
Comparator<Student> com=new BiJiao();
TreeSet<Student> st=new TreeSet<>(com) ;//一旦指定外部构造器,那么就会按照外部构造器来比较
st.add(new Student(12,"c莉莉"));
st.add(new Student(8,"a莉莉"));
st.add(new Student(4,"g莉莉"));
st.add(new Student(9,"b莉莉"));
st.add(new Student(10,"e莉莉"));
st.add(new Student(11,"f莉莉"));
st.add(new Student(1,"d莉莉"));
System.out.println(st.size());
System.out.println(st);
}
}
实际开发中利用外部比较器多,因为扩展性好(多态)
换一种写法
//利用外部比较器,必须自己指定
/*Comparator<Student> com=new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
};*/
TreeSet<Student> st=new TreeSet<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.getName().compareTo(o2.getName());
}
}) ;
【5】TreeSet底层的二叉树的遍历是按照升序的结果出现,的这个升序是靠中序遍历得到的:
【十九】Map接口
【1】常用方法:
public class Test01 {
public static void main(String[] args) {
/*
增加:put(K key, V value)
删除:clear() remove(Object key)
修改:
查看:entrySet() get(Object key) size() values()
判断:containsKey(Object key) containsValue(Object value)
equals(Object o) isEmpty()
*/
//创建一个Map集合: 无序,唯一
Map<String,Integer> map=new HashMap<>();
map.put("莉莉",10101010);
map.put("娜娜",123456789);
map.put("美美",105894156);
map.put("菲菲",1010147890);
map.put("果果",10101010);
map.put("莉莉",10101010);
map.put("哒哒",10101010);
/*map.clear();//清空 */
/*map.remove("菲菲");//移除 */
System.out.println(map.size());
System.out.println(map);
System.out.println(map.containsKey("莉莉"));
System.out.println(map.containsValue(123456789));
Map<String,Integer> map2=new HashMap<>();
map2.put("莉莉",10101010);
map2.put("娜娜",123456789);
map2.put("美美",105894156);
map2.put("菲菲",1010147890);
map2.put("果果",10101010);
map2.put("莉莉",10101010);
map2.put("哒哒",10101010);
System.out.println(map == map2);
System.out.println(map.equals(map2));//equals进行了重写,比较的集合中的值是否一致
System.out.println(map.isEmpty());
System.out.println(map.get("果果"));
System.out.println("--------------------");
//对集合中的ley进行遍历
Set<String> set = map.keySet();
for(String s:set)
{
System.out.println(s);
}
//对集合中的values进行查看
Collection<Integer> values = map.values();
for(Integer i:values){
System.out.println(i);
}
//get(Object key) keySet()
Set<String> set2 = map.keySet();
for(String s:set2)
{
System.out.println(map.get(s));
}
//
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for(Map.Entry<String, Integer> e : entries){
System.out.println(e.getKey() + "-------" + e.getValue());
}
}
}
【2】Hashtable,LinkedHashMap的使用:
Map<String,Integer> map=new LinkedHashMap<>();
map.put("莉莉",10101010);
map.put("娜娜",123456789);
map.put("美美",105894156);
map.put("菲菲",1010147890);
map.put("果果",10101010);
map.put("莉莉",10101010);
map.put("哒哒",10101010);
/*map.clear();//清空 */
/*map.remove("菲菲");//移除 */
System.out.println(map.size());
System.out.println(map);
【3】TreeMap的使用:
特点:唯一,有序(按照升序或者降序)
原理:二叉树,key遵照二叉树的特点放入集合的key的数据对应的类型内部一定要实现比较器(内部比较器,外部比较器二选一)
【1】key的类型为String类型:
public class Test02 {
public static void main(String[] args) {
Map<String,Integer> map=new TreeMap<>();
map.put("alili",1235);
map.put("dlili",8963);
map.put("blili",89841);
map.put("glili",1956);
map.put("clili",189163);
map.put("flili",11981);
map.put("elili",18774);
System.out.println(map.size());
System.out.println(map);
}
}
【2】key的类型为自定义的引用数据类型
(1)内部比较器:
public class Student implements Comparable<Student>{
private int age;
private String name;
private double height;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student(int age, String name, double height) {
this.age = age;
this.name = name;
this.height = height;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", height=" + height +
'}';
}
@Override
public int compareTo(Student o) {
/*return this.getAge()-o.getAge();*/
return this.getName().compareTo(o.getName());
}
}
public class Test03{
public static void main(String[] args) {
Map<Object, Object> map = new TreeMap<>();
map.put(new Student(19,"b莉莉",170),1001);
map.put(new Student(19,"b莉莉",170),1001);
map.put(new Student(18,"a莉莉",180),1001);
map.put(new Student(19,"c莉莉",170),1001);
map.put(new Student(17,"d莉莉",170),1001);
System.out.println(map);
System.out.println(map.size());
}
}
(2)外部比较器:
public class Student{
private int age;
private String name;
private double height;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getHeight() {
return height;
}
public void setHeight(double height) {
this.height = height;
}
public Student(int age, String name, double height) {
this.age = age;
this.name = name;
this.height = height;
}
@Override
public String toString() {
return "Student{" +
"age=" + age +
", name='" + name + '\'' +
", height=" + height +
'}';
}
}
public class Test03{
public static void main(String[] args) {
Map<Student, Integer> map = new TreeMap<>(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return ((Double)(o1.getHeight())).compareTo((Double)(o2.getHeight()));
}
});
map.put(new Student(19,"b莉莉",170),1001);
map.put(new Student(19,"b莉莉",170),1001);
map.put(new Student(18,"a莉莉",180),1001);
map.put(new Student(19,"c莉莉",170),1001);
map.put(new Student(17,"d莉莉",170),1001);
System.out.println(map);
System.out.println(map.size());
}
}
【二十】HashMap
【1】HashMap中重要属性
【2】HashMap中的重要属性:
(1)HashMap的K,V的值,在创建对象的时候确定:K:Integer V:String
(2)HashMap的父类AbstractMap已经实现类Map接口,但是源码中又单独实现了Map接口 这个操作就是一个多余的操作 集合的创作者已经承认
//重要属性:
static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16 定义数组
static final int MAXIMUM_CAPACITY = 1 << 30; //定义一个很大很大的数
static final float DEFAULT_LOAD_FACTOR = 0.75f; //定义了一个值:0.75 负载因子,加载因子
transient Entry<K,V>[] table ;// 底层主数组
transient int size; //添加的元素的数量
int threshold; //定义个变量,没赋值默认为0, 这个变量是用来表示数组扩容的边界值,门槛值
final float loadFactor;//这个变量用来接受:填装因子,负载因子,加载因子
【3】HashMap的构造器
【4】HashMap的两个经典面试题
【5】TreeMap底层原理:
public class TreeMap<K,V>{
//重要属性
//外部比较器:
private final Comparator<? super K> comparator;
//树的根节点:
private transient Entry<K,V> root;
//集合中元素的数量:
private transient int size = 0;
//空构造器:
public TreeMap() {
comparator = null;//如果使用空构造器,那么底层就不时使用外部比较器
}
//有参构造器:
public TreeMap(Comparator<? super K> comparator) {
this.comparator = comparator;//如果使用有参构造器,相当于指定使用了外部比较器。
}
}
【6】TreeSet底层源码:
public class TreeSet<E> extends AbstractSet<E>
implements NavigableSet<E>, Cloneable, java.io.Serializable
{
//重要属性:
private transient NavigableMap<E,Object> m;
private static final Object PRESENT = new Object();
//在调用空构造器的时候,底层创建了一个TreeMap
public TreeSet() {
this(new TreeMap<E,Object>());
}
TreeSet(NavigableMap<E,Object> m) {
this.m = m;
}
public boolean add(E e) {
return m.put(e, PRESENT)==null;
}
}
【7】Collections工具类
public class Test1 {
public static void main(String[] args) {
//Collections不支持创建对象,因为构造器私有化。
/*Collections cols=new Collections();*/
//里面的属性和方法都是被static修饰,我们可以直接用类名.去调用即可。
//常用方法:
//addAll:
ArrayList<String> list = new ArrayList<>();
list.add("aa");
list.add("bb");
list.add("cc");
Collections.addAll(list,"dd","ee","ff");
Collections.sort(list);//sort提供的是升序排列
//binarySearch必须在有序的集合中查找: --》排序
System.out.println(list);
System.out.println(Collections.binarySearch(list, "cc"));
//copy
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list2,"tt","ss","gg");
Collections.copy(list,list2);//将list2的内容
System.out.println(list);
System.out.println(list2);
//fill 填充
Collections.fill(list2,"yy");
System.out.println(list2);
}
}