泛型机制
泛型是JavaSE5.0引入的新特性,它的本质是参数化类型。在类、接口和方法的定义过程中,所操作的数据类型通过传入的
参数进行指定。广泛应用在集合框架中,所有的集合类型都带有泛型参数,
创建集合对象时可以直接指定放入集合中的元素类型,Java编译器可以根据此类型进行类型检查,可以减少代码在运行时出现的错误可能性
为什么要有泛型?
现在有以下的实例要求: 现在要求设计一个可以表示出坐标点的类,坐标由X和Y组成,坐标的表示方法有以下三种: 整数表示:x = 10、y = 20 小数表示:x = 10.5、y = 20.6 字符串表示:x = "东经180度"、y = "北纬210度" 问:此类该如何设计?
class Point<T> { // 此处可以是任意的标识符号,T是type的简称
private T var; // 此变量的类型由外部决定
public T getVar() { // 返回值的类型由外部指定
return var;
}
public void setVar(T var) {// 设置的类型由外部指定
this.var = var;
}
};
public class GenericsDemo05 {
public static void main(String[] args) {
Point<Integer> p = new Point<Integer>() ; // 里面的var类型为Integer类型
p.setVar(10) ; // 设置数字,自动装箱
System.out.println(p.getVar() * 2); // 计算结果,按数字取出
}
}
泛型可以解决数据类型的安全性问题,它主要的原理,是在类声明的时候通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化的时候只要指定好需要的类型即可。
在泛型的指定中,是无法指定基本数据类型的,必须设置成一个类,这样在设置一个数字的时候就必须使用包装类,而在JDK 1.5之后提供了自动装箱的操作,操作时也不会太复杂。
泛型类定义格式: [访问权限] class 类名称<泛型类型1,泛型类型2,…泛型类型3>{
[访问权限] 泛型类型标识 变量名称 ;
[访问权限] 泛型类型标识 方法名称(){} ;
[访问权限] 返回值类型声明 方法名称(泛型类型标识 变量名称){} ;}
泛型对象定义 类名称<具体类> 对象名称 = new 类名称<具体类>() ;
如果一个类中有多个属性需要使用不同的泛型声明,则可以在声明类的时候指定多个泛型类型。
集合
java中的集合框架提供了一套设计优良的接口和类,使程序员操作成批的数据或对象元素极为方便。这些接口和类有很多对抽象数据类型操作的API,这是我们常用的且在数据结构中熟知的,例如:Maps,Sets,Lists,Arrays等,并且Java用面向对象的设计对这些数据结构和算法进行了封装,这极大地减轻了程序员编程时的负担。程序员也可以以这个集合框架为基础,定义更高级别的数据抽象,比如栈,队列和线程安全的集合等,从而满足自己的需要。 在日常编程中,经常需要对多个数据进行存储。从传统意义上讲,数组是一个很好的选择,但是一个数组经常需要指定好长度,且这个长度是不可变得。这时我们需要一个可以动态增长的“数组”,而java的集合类就是一个很好的设计方案。
集合(Collection)是用来管理一组对象的单一对象。集合内的对象被称之为元素(elements)。通常,集合可以处理很多种类型的对象,这些对象都有一个共同的父类Object。Java SE 5.0以前,集合的元素只要是Object类型就行,那个时候任何对象都可以存放在集合内,但是从集合中获取对象后,需要进行正确的强制类型转换。但是,Java SE 5.0 以后,可以使用新特性”泛型”,
用来指定要存放在集合中的对象类型。避免了强制转换的麻烦。
数组与集合
数组的优点:
1.数组可以快速地随机访问数组中的元素。数组可以保存基本类型和对象。
2.数组的容量固定。当数组空间不足的时候需要做数据迁移,效率很低。
3.经常不知正在编写的程序需要使用多少个对象。
相同点:都是存储数据的容器,存储Object类型时,其实存储的都是对象的引用(地址)
不同点:1.数组可以存储基本数据类型,集合不可以;
2.数组的长度固定,对象数量未知时不适合使用,集合的长度可变,适用性比较广。
集合类
集合类存放于java.util包中。
集合类存放的都是对象的引用,而非对象本身,出于表达上的便利,我们称集合中的对象就是指集合中对象的引(reference)。
集合类型主要有3种:set(集)、list(列表)和map(映射)。
java的集合框架
java集合框架主要提供三种类型的集合(Set,List,Map)和一个迭代器。
Iterator迭代器
在类集中提供的常见输出方式:
Iterator:迭代输出,是使用最多的输出方式;
foreach:JDK 1.5之后提供的新功能,可以输出数组或集合。
迭代器的作用是用于遍历集合元素,它是一个接口,Collection接口中提供了获取迭代器对象的方法,集合输出的标准操作: “在使用集合输出的时候必须形成以下的一个思路:‘只要是碰到了集合输出的操作,就一定使用Iterator接口’,因为这是最标准的做法” Iterator接口的操作原理: Iterator是专门的迭代输出接口,所谓的迭代输出就是将元素一个个进行判断,判断其是否有内容,如果有内容则把内容取出
方法 |
描述 |
public boolean hasNext() |
判断是否有下一个值/判断集合是否还有元素可以遍历 |
public E next() |
取出当前元素/返回迭代的下一个元素 |
public void remove() |
移除当前元素 |
使用迭代器遍历集合时,不能通过集合的remove方法删除集合元素,否则会抱异常。我们需要通过迭代器自身提供的remove方法来删除通过next()迭代出来的元素,需要注意的是先next,再remove.
使用Iterator删除指定内容
package org.lxh.demo13.iteratordemo;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
List<String> all = new ArrayList<String>(); // 实例化List接口
all.add("hello"); // 增加元素
all.add("_"); // 增加元素
all.add("world"); // 增加元素
Iterator<String> iter = all.iterator(); // 直接实例化Iterator接口S
while (iter.hasNext()) { / 依次输出
String str = iter.next(); // 取出内容
if ("_".equals(str)) { // 判断内容是否是“_”
iter.remove(); // 删除当前元素
} else {
System.out.print(str + "、");// 输出元素内容
}
}
System.out.println("\n删除之后的集合:" + all); // 输出集合内容,调用toString()方法
}}
List(英文:线性表,列表的含义)
List 是一个元素有序、且可重复的集合,集合中的每个元素都有其对应的顺序索引,从0开始
List 允许使用重复元素,可以通过索引来访问指定位置的集合元素。
List 默认按元素的添加顺序设置元素的索引。
List 集合里添加了一些根据索引来操作集合元素的方法
ArrayList
ArrayList是实现了基于动态数组的数据结构,对象存储在连续的位置上
ArrayList是List子类,可以直接通过对象的多态性,为List接口实例化 此类的定义如下:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, Serializable
AbstractList类的定义如下:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E>
容量可以动态变化,也被称为动态数组。 快速的随机存取。中间插入和删除元素时,速度较慢。
LinkedList
LinkedList表示的是一个链表的操作类,此类定义如下:
public class LinkedList<E> extends AbstractSequentialList<E>implements List<E>, Queue<E>, Cloneable, Serializable
LinkedList基于双链表的数据结构,链表中的每个节点都包含了前一个和后一个元素的引用。对于随机访问get和set,ArrayList绝对优于LinkedList,因为LinkedList要移动指针。对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
方法 | 描述 |
public void addFirst(E o) |
在链表开头增加元素 |
public void addLast(E o) |
在链表结尾增加元素 |
public boolean offer(E o) |
将指定元素增加到链表的结尾 |
public E removeFirst() |
删除链表的第一个元素 |
public E removeLast() |
删除链表的最后一个元素 |
优缺点:
ArrayList:在随机访问集合时,效率高。 根据下标寻找时,linkedList需要移动指针
LinkedList:在做增删操作时,效率高 。当集合中的元素非常多时,效率高低比较明显
Queue
Queue接口是Collection的子接口,此接口定义如下:
public interface Queue<E> extends Collection<E>
队列Queue是常用的数据结构,可以将队列看成特殊的线性表,队列限制对线性表的访问方式:只能从一端添加(offer)元素,从另一端取出(poll)元素。
队列遵循先进先出(FIFO first Input First Output)的原则
实现类LinkedList也实现了该接口,选择此类实现Queue的原因在于Queue经常要进行添加和删除操作,而LinkedList在这方面效率比较高。
方法 | 描述 |
public E element() | 找到链表的表头 |
public boolean offer(E o) | 将指定元素增加到链表的结尾 |
public E peek() | 找到但并不删除链表的头 |
public E poll() | 找到并删除此链表的头 |
public E remove () | 检索并移除表头 |
双端队列Deque
Deque是Queue的子接口,定义了所谓的”双端队列”,即从队列的两端分别可以入队(offer)和出队(poll)。
同样,LinkedList实现了该接口
Stack类栈:
Stack继承自Vector,实现一个后进先出的堆栈。Stack提供5个额外的方法使得Vector得以被当作堆栈使用。基本的push和pop(出栈)方法,还有peek方法得到栈顶的元素,empty方法测试堆栈是否为空,search方法检测一个元素在堆栈中的位置。Stack刚创建后是空栈。使用双端队列,来禁止队尾的操作,只对队首进行操作:
Deque<String> stack = new LinkedList<String>();
push(E e): 表示从栈的顶部进入栈内
E pop():表示从栈的顶部移除元素