java基础第17天



常见数据结构


1 常见数据结构有哪些
 栈、队列、数组、链表


2 栈(Last In First Out)
  后进先出,最上面的元素我们叫栈顶元素!
  出栈(弹栈),就是把栈顶元素从栈中移除,并返回。
  压栈,就是把一个新元素放到栈顶位置。
  相当于:大缸
  


3 队列(FIFO)
  先进先出!
  相当于:没底的管道!


4 数组
  数组索引元素的速度天下无敌!
  arr[10] – 快速最快!
  如果我们需要向数组插入和删除元素,那么就需要copy数组。速度慢。


5 链表
  链表元素在内存中不是邻居,链表头元素知道下一个元素的地址,下一个元素又知道下下元素 的地址,最后一个元素,即链表尾,它的下一个元素是null。
  链表的索引速度很慢,但插入和删除元素快速很快。


List实现类


1 常用List实现类
  ArrayList
  LinkedList
  Vector
  Stack


2 ArrayList存储String数据
创建ArrayList
创建字符串对象(多个)
把字符串对象添加到ArrayList
使用迭代器遍历集合对象
使用get()方法遍历集合对象
尝试向集合中添加int类型


3 ArrayList存储自定义类型(Person)数据
创建ArrayList
创建Person类对象(多个)
把Person对象添加到ArrayList对象中
使用迭代器遍历集合对象
使用get()方法遍历集合对象


4 LinkedList特有方法
void addFirst(Object)
void addLast(Object)
Object getFirst()
Object getLast()
Object removeFirst()
Object removeLast()
void push(Object o):压栈,就是把元素添加到0下标位置,与addFirst一样。
Object pop():弹栈,就是把0下标位置的元素返回并移除。


5 LinkedList存储String类型数据
  参考ArrayList存储String类型数据


6 LinkedList存储自定义(Person)类型数据




7 LinkedList特有方法测试
创建LinkedList对象
创建String对象(多个)
把String对象添加到LinkedList对象中
测试向集合头部添加元素:addFirst(Object)
测试向集合尾部添加元素:addLast(Object)
测试获取集合头部元素:getFirst()
测试获取集合尾部元素:getLast()
测试移除集合头部元素:removeFirst()
测试移除集合尾部元素:removeLast()


8 Vector特有方法
  底层用的是数组,与ArrayList相同!
  Vector中的方法都是同步的,即Vector是线程安全的。
  ArrayList是线程这安全的。
void addElement(Object):等同与add()
Object elementAt(int):等同与get()
Object removeElement(Object):等同与remove()
Enumeration elements():返回枚举器,它是老版本的迭代器!


9 Vector存储String数据




10 Vector存储自定义(Person)类型数据




11 Vector特有方法测试




12 遍历Vector对象
Iterator遍历
Enumeration遍历


13 去除ArrayList中重复元素(String)
  编写toSingletonList()方法
  参数:List,待处理的集合(包含重复元素的集合)
  返回:List,处理后的集合(不包含重复元素的集合)
创建ArrayList对象
创建String对象(多个,包含重复对象)
把String对象添加到ArrayList对象中
调用toSingletonList()方法
创建一个新集合对象
遍历老集合对象
查看新集合对象中是否包含元素
包含就不添加,不包含就添加到新集合中
返回新集合。


14 去除ArrayList中重复元素(自定义)
发现没有去除重复元素
测试String类的equals()方法
测试Person类的equals()方法
contains()方法依赖元素的equals()方法
为Person添加equals()方法


使用LinkedList编写栈和队列


编写MyStack
要求MyStack类的方法:
boolean empty()
Object pop()
void push(Object o)
乾坤大挪移!
  使用一个LinkedList对象做属性,使用它来存储数据。本类所有方法都针对这个属性进行操作。也就是把LinkedList封装起来,外界对Stack施加的操作都转移到对本类属性LinkedList之上。


2 测试MyStack
创建MyStack类的对象
创建String对象(多个)
调用push()方法把String对象压栈(多个)
使用循环(条件使用empty())调用pop()方法出栈


3 编写MyQueue
  要求MyQueue类的方法:
boolean empty()
void add(Object o)
Object poll()
与编写MyStack类一样,也是内部使用LinkedList为属性,所有方法都针对属性进行操作。


4 测试MyQueue
创建MyQueue类的对象
创建String对象(多个)
调用push()方法把String对象添加到队列中(多个)
使用循环(条件使用empty())调用poll()方法出列。


泛型
  java中泛型应用最多的地方是集合类。
1 定义数组和集合的比较
  在定义数组时都需要指定元素的类型,例如:
  String[] arr = new String[10];
  对于arr而言,只能为元素指定String类型的值,而其他类型不行。
  -------------------
  我们以前定义的集合对象都是可以存储任意类型(Object)的对象的。就像是在使用Object类 型的数组一样。但这有一个缺点,就是存入后再取出的元素需要强转。这也是可能发生 ClassCastExcetpion的地方。
  -------------------
  大多数情况下,我们不会对同一个集合对象添加多种类型的元素,所以我们最希望可以像使用数组一样,在定义集合对象时指定可以装载的元素类型。


4 什么是泛型类
  具有一个或多个类型参数的类就是泛型类。
  java中所有集合类都是泛型类。
  在定义泛型类对象时,可以指定装载的元素类型。就像定义数组时可以指定元素类型一样。


5 泛型的好处
  将运行期遇到的问题转移到了编译期。
  坏处也不少!!!
  Java没有类模板的概念,为了添加这一概述,就出现了泛型。这也是为了兼容性迁移的第一步吧。泛型被称为四不像!这可能需要一个很慢长的迁移的过程,也许将来会完善的!相对C++而言,Java的泛型可能是败笔了。


6 使用泛型类
  泛型类中使用的类型参数都被实际参数赋值。
  使用泛型集合类


7 泛型类需要给类型参数赋值
  类型参数是变量,你要给类型参数赋值。当然这个值必须是个类型。
  定义泛型类一定要给类型参数赋值,不然就等同于赋值为Object。
  在泛型类中使用的类型参数都会被赋的值替代。例如定义的属性或方法中使用的泛型参数都被替换了。
  因为在定义泛型类时,不知道用户指定的类型值是什么,所以类中使用类型变量的属性和方法都不能确定,那么也就不能去调用类型不确定对象的方法(但可以调用Object类具有的方法)。


8 继承泛型类时的两种赋值方式
  给出类型常量
  给出类型变量


9 定义泛型接口




10 实现泛型接口两种赋值方式
  给出类型常量
  给出类型变量


泛型方法


1 泛型方法
  java中不只可以定义泛型类,还可以定义泛型方法。
  泛型方法被调用时需要指定泛型参数的值,这一点与创建泛型类的对象一样。
  泛型方法可以定义在泛型类中,也可以定义在非泛型类中。泛型方法可以是static的,也可以不是static的。这与泛型和static没有半毛钱关系!


2 定义泛型方法
  定义泛型方法的格式为:
  修饰符 <类型参数> 返回值 方法名(参数列表)
  例如:
  public class MyClass {
    public <T> void fun(T t) {
          System.out.println(t.getClass().getName());
    }
  }
  通常泛型方法的参数都会使用类型变量。上例中的参数就使用类型变量T!在使用者调用这个方法时会给T赋值,那么参数t的类型也就确定了。


3 调用泛型方法
  通常泛型方法的参数都会使用类型变量,所以一般不需要显示为泛型方法指定类型参数的值。而是通过调用时传递的实际参数的类型隐示给类型参数赋值的。例如:
  MyClass mc = new MyClass();
  mc.fun(“hello”);
  因为调用fun()方法时实参的类型为String,那么这就隐示为fun()方法的类型参数赋值为String了。
  当然也可以显示指定泛型方法的类型参数,这需要在点前面指定类型值。例如:
  mc.<String>(“hello”);


泛型边界


1 泛型边界限定的类型值的范围
  通常我们看到的泛型类都没有边界限定,也就是说可以给泛型类的类型变量赋任意类型的值(当然基本类型是不可以的)。
  java允许给类型参数指定边界,这样在指定类型参数的值时就必须在边界之内。


2 带有边界的泛型类
  带胡边界的泛型类需要使用extends关键字为类型变量指定边界,格式如下:
  class 类名<变量名 extends 边界类型> {…}
  例如:
  class MyClass<T extends Person> {…}


3 创建带有边界的泛型类对象
  MyClass mc = new MyClass();//在没有指定泛型参数的值时,类型变量的值就是边界类型了。也就是Person类型了。
  因为MyClass类指定了边界Person,那么只能为类型变量赋值为Person或Person的子类了。
  MyClass<Student> mc =new MyClass<Student>();


4 多个边界类型
  还可以为类型参数指定多个边界类型,格式为:
  class 类名<类型变量名 extends 边界类型1 & 边界类型2…> {…}
  当为类型变量指定多个边界类型时,那么最多只能有一个是类类型,其他的都必须是接口类型。这个道理就不用说了吧。


通配符


1 错误理解泛型
  现在有Person和Student类,其中Student是Person的子类。这说明:
  Person p = new Student();
  是正确的。
  
  下面代码编译也是通过的,但运行时会抛出ArrayStoreException。
  
Person[] ps = new Student[10];
ps[0] = new Student();
ps[1] = new Employee();
  
  在泛型中java就不在允许这么方式了:
ArrayList<Person> list = new ArrayList<Student>();
  上面代码是编译不通过的,也就是说Person虽然是Student的父类,但ArrayList<Person>不是ArrayList<Student>的父类。
  这也就是说,不能使用ArrayList<Student>类型的对象来调用参数为ArrayList<Person>类型的方法了。这真是太可惜了!
  如果现在有方法用来打印封装Person的集合对象,即参数类型为List<Person>。但我们不能让这个方法打印List<Student>,这不是很可惜么?
public static void fun(List<Person> list) {
for(Person p : list) {
System.out.println(p);
}
}




2 子类型通配符
  为了处理上面的问题,java提供了通配符。尝试把上面的fun()方法参数类型修改为List<? extends Person>类型:
public static void fun(List<? extends Person> list) {
for(Person p : list) {
System.out.println(p);
}
}


  我们可以把List<Student>、List<Employee>、List<Person>类型的对象赋值给List<? extends Person>类型的引用。
  ?并不是变量,而是确定的值。它表示一种确定下来的值,但我们不知道它是什么类型罢了。也就是说List<? extends Person>的元素类型已经开始不明确了,我们只知道元素类型是Person或Person的某种子类型,但具体是哪一个类型。这也说明我们不能向它添加东西了。
  
List<? extends Person> list1 = new ArrayList<Student>();//ok
List<? extends Person> list2 = new ArrayList<Employee>();//ok
list1.add(new Student());//error
list1.add(new Employee());//error
list2.add(new Student());//error
list2.add(new Employee());//error


  上面代码中,对list1、list2添加什么元素都是错误的。让我们分析一下,对编译器而言,它不知道list1、list2指向的是实体是什么类型。如果允许了list1.add(new Student()),那么说明也要允许list1.add(new Employee())。因为对list1这个引用而言,它是List<? extends Person>类型,元素类型表示一种不知道的但却是确定的类型,但只知道它是Person的子类型。如果可以添加Student,那为什么不能添加Employee呢?所以编译器的态度是添加什么东西都不行!
  也就是说,当给类型参数E赋值为<? extends 类型>时,那么对于参数为E的方法就不能再使用了,因为传递什么类型的参数都是错误的。


3 父类型通配符
  还可以给类型参数赋值为<? super 类型>,这就是父类型通配符。
  如果你需要一个方法,给参数List添加元素。
  
public static void fun(List<Person> list, Person p) {
list.add(p);
}
  
  你不能使用List<Student>来调用上面的方法,这个道理我们应该已经明白了。但你可能会说可以把方法参数类型修改为List<? extends Person>类型,这样就可以把List<Person>类型的值传递给参数了。但是如果参数类型为List<? extends Person>类型,那么就不能调用add()方法了!!!
  这时可以把参数类型指定为List<? super Student>类型,你可以把List<Student>、List<Preson>,甚至List<Object>类型的对象赋值给它。
  List<? extends Person>表示元素类型为不知道的确定类型,只知道元素类型为Student的父类型,但不知道是哪个父类型!也许是Person,也许是Object类型。无论是哪一种父类型,说明我们可以向集合对象添加Student对象!因为把Student类型对象添加到List<Person>或List<Object>中都是可以的!
  但要注意,这时你就不能再使用get()方法了,因为元素的类型不知道是什么,只知道是Student类型的父类型,哪一种就不知道了,这说明你不能用Peson引用指向get()方法的返回值。
  但可以使用Object引用来指向get()方法的返回值,因为Object是所有类的父类!


4 无界通配符
  无界通配符:List<?>!
  你可以使用List<?>的引用指向List<? extends Person>、List<? super Stuent>、List<Person>、List<Student>等等,即没有指定泛型的List可以指向什么,它就可以引向什么。
List<?> list1 = new ArrayList();
List<?> list2 = new ArrayList<Object>();
List<? extends Person> pList = new ArrayList<Person>();
List<?> list3 = pList;
List<? super Student> sList = new ArrayList<Student>();
List<?> list4 = sList;


  呵呵~,你不能使用List<?>引用调用add()方法,也只能使用Object引用来指向get()方法的返回值了。
  其实大多数情况下List<?>就是一种装饰!基本与List一样,没有什么区别了!但如果你使用List会有警告,而使用List<?>时就没有警告了!因为傻瓜编译器看到了你使用List<?>说明你在使用泛型的集合类,它会很开心!


5 通配符总结
  可以使用List<? extends Person>指向List<Person>、List<Student>、List<Employee>。只要元素类型为Person,或其子类就是可以的。
  可以使用List<? super Student>指向List<Student>、List<Person>、List<Object>。只要元素类型为Student或其Student父类就是可以的。
  小心,你不能在new时使用通配符:
  new ArrayList<? extends Person>(),这是不可以的!
  但可以在定义引用时使用通配符:
  ArrayList<? extends Person> list;


四不像的泛型
  泛型只是编译器状态!!!
  所有泛型类在JVM那里都是普通类!也就是说ArrayList<Person>和ArrayList<Student>在JVM那里都是ArrayList而已!也就是说元素的类型还是Object类型,一切的一切都是编译器的要求!
  其实对泛型还有很多限制!这里就不在多说了!





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值