Java——数据结构接口
本人是个新手,写下博客用于自我复习、自我总结。
如有错误之处,请各位大佬指出。
参考教材:零基础学Java
基本结构:
①集合:数据元素同属一个集合
②线性结构:数据元素间存在一对一的关系
③树形结构:结构中元素间是一对多的关系
④图(网)状结构:结构中元素间是多对多的关系
数据还分为逻辑结构和物理结构。
①逻辑结构:数据元素之间存在的关系(逻辑关系)称为数据的逻辑结构
②物理结构:数据结构在计算机中的表示称为数据的物理结构
数据结构有很多的接口,它规定了此种数据结构如何存储数据。
例如队列接口:
interface Queue{
void add(Object obj); //向队尾插入元素
Object remove(); //从队头删除元素
int size(); //查看队列的长度
}
可以通过实现这个接口,将数据存储到这种数据结构中,以方便存取。
例:
import java.util.*;
public class DataStructureInterface {
public static void main(String args[]){
ArrayList al=new ArrayList();
al.add("zzz");
al.add("zst");
al.add("cxk");
for(int i=0;i<al.size();i++){
System.out.println(al.get(i));
}
}
}
1)Collection集合接口:
Collection接口是数据集合接口,它位于数据结构API的最上部。构成Collection的单位,被称之为元素。接口提供了添加、删除元素等管理数据的功能。根据数据管理的方法不同,可将Collection接口分成为3个部分,分别是:Map接口、Set接口、List接口
所有实现Collection接口的类都必须提供两个标准的构造函数:
*无参数的构造函数,用于创建一个空的Collection
*带Collection参数的构造函数,用于创建一个新的Collection。
这个新的Collection与传入的Collection拥有相同的元素。
(带参数的构造函数主要用来实现复制一个Collection)
如何遍历Collection中的每一个元素?不论Collection的实际类型如何,它都支持iterator()方法,该方法返回迭代器。使用该迭代器即可访问Collection中的每一个元素。
如:
Iterator it=collection.iterator(); //获得一个迭代器
while(it.hasnext()){
Object obj=it.next(); //得到下一个元素
}
List接口和Set接口是两个非常有用的集合接口,数组和列表等数据结构,基本上都是从这两个接口实现而来。
1.List接口:
List接口是有序的Collection,使用此接口能够精确地控制每个元素插入的位置。用户能够使用索引(元素在List中的位置,类似于数组下标)来访问List中的元素,这类似于数组。除了具有Collection接口必备的iterator()方法外,List还提供listIterator()方法,其返回ListIterator接口。和标准的Iterator接口相比,ListIterator多了一些add()之类的方法,允许添加、删除、设定元素,还能向前和向后遍历。
实现List接口的常用类有LinkedList和ArrayList。
*LinkedList:链表类
LinkedList实现了List接口,允许null元素。此外LinkedList在首部或尾部提供额外的get()、remove()、insert()等方法。LinkedList没有同步方法,如果多个线程同时访问一个List,则必须自己实现访问同步,一种解决方法是在创建List时构造一个同步的List。
List list=Collection.synchronizedList(new LinkedList(…));
*ArrayList:数组列表类
ArrayList实现了可变大小的数组,它允许存储所有元素,包括null。ArrayList没有同步。每个ArrayList实例都拥有一个容量(Capacity)属性,即存储元素的数组的大小,这个容量可随着不断添加新元素而自动增加,但是增长算法并没有定义。当需要插入大量元素时,在插入前可调用ensureCapacity()方法来增加ArrayList的容量,以提高插入效率。和LinkedList一样,ArrayList也是非同步的。
2.Set接口:
Set是一种不包含重复元素的Collection,即任意的两个元素e1和e2比较,结果都不相等。Set最多有一个null元素。实现Set接口的常用类有HashSet和TreeSet
*HashSet集合类
HashSet类实现了Set接口,允许null元素。在具体使用该类时,要特别注意其不保证集合的迭代顺序,即不保证该顺序恒久不变。关于该集合类的基本操作包括add()、remove()、contains()、size()等方法。
*TreeSet集合类
TreeSet类实现了Set接口,与HashSet类相比,该类实现了排序功能。存储在该集合中的元素默认按照升序排序元素,或者根据使用的构造方法不同,可能会按照元素的自然顺序进行排序,或者按照在创建Set集合时所提供的比较器进行排序。
3.Map映射接口:
Map没有继承Collection接口,其提供key到value的映射。Map中不能包含相同的key,每个key只能映射一个value。Map接口提供3种集合的视图,Map的内容可以被当作一组key集合、一组value集合或一组key-value映射。
*Hashtable散列表类
Hashtable继承Map接口,实现一个key-value映射的散列表。任何非空(non-null)的对象都可作为key或value。添加数据使用put(key,value)方法,取出数据使用get(key)方法,这两个基本操作的时间开销为常数。
Hashtable通过initial capacity和load factor两个参数调整性能。通常默认的load factor 0.75较好地实现了时间和空间的均衡,增大load factor可以节省时间,但相应的查找时间将增大,这会影响像get和put这样的操作。
例:
Hashtable numbers=new Hashtable();
numbers.put("one",new Integer(1));
numbers.put("two",new Integer(2));
numbers.put("three",new Integer(3));
//如果想取出2,可以用相应的key
Integer n=(Integer)numbers.get("two");
System.out.println("two="+n);
作为key的对象,将通过计算其散列函数确定与之对应的value位置,因此任何key的对象都必须实现hashCode()和equals()方法。hashCode()和equals()方法继承于根类Object。如果用自定义的类当作key,则要相当小心。按照散列函数的定义,如果两个对象相同,即obj1.equals(obj2)=true,则它们的hashCode必须相同。但如果两个对象不同,则它们的hashCode不一定不同。如果两个不同对象的hashCode相同,这种现象称为冲突。冲突会导致操作散列表的时间开销增大,所以尽量定义好的hashCode()方法,能加快散列表的操作。
所以注意:要同时复写equals()方法和hashCode()方法,而不要只写其中的一个。Hashtable是同步的。
*HashMap散列映射类
HashMap和Hashtable类似,但是HashMap是非同步的,并且允许null,即null value和null key。如将HashMap视为Collection时(values()方法可以返回Collection),其迭代器操作时间开销和HashMap的容量成比例。因此,迭代操作的性能相当重要,切记不要将HashMap的初始容量设得过高,或者将load factor设得过低。
2)Iterator迭代器接口:实现各种集合元素的读取。
什么是迭代器?迭代器指向两个元素中间的位置,当调用hasNext()方法时,如果返回true,此时调用next()方法返回下一元素。迭代器指向下两个元素之间的位置,如果要删除下一个元素,必须先调用next()方法,再调用remove()方法。
Iterator接口中定义了以下3个方法:
hasNext():是否还有下一个元素
next():返回下一个元素
remove():删除当前元素
迭代器的作用:
有些接口类没有提供get()操作,这时可用迭代器来获得信息。所有Collection接口的子类、子接口都支持Iterator迭代器。迭代器模式又称为游标模式。
官方定义:提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节,这就是迭代器。
迭代器模式:由以下角色组成。
(1)迭代器角色:负责定义访问和遍历元素的接口迭代器角色定义了遍历的接口,但是没有规定由谁来控制迭代。在JavaCollection的应用中,由客户程序来控制遍历的进程,被称为外部迭代器。还有一种实现方式是由迭代器自身来控制迭代,被称为内部迭代器。外部迭代器要比内部迭代器灵活,强大,而且内部迭代器在Java中,可用性很弱
(2)具体迭代器角色:实现迭代器接口,并记录遍历中的当前位置
(3)容器角色:负责提供创建具体迭代器角色的接口
(4)具体容器角色:实现创建具体迭代器角色的接口————这个具体迭代器角色与该容器的结构相关
注:在迭代器模式中,没有规定谁来实现遍历算法。因为既可以在一个容器上使用不同的遍历算法,也可以将一种遍历算法应用于不同的容器。这样就破坏了容器的封装————容器角色就要公开自己的私有属性。那把它放入容器角色里实现,这样迭代器角色被架空,仅仅具备存放一个遍历当前位置的功能,但是遍历算法和特定的容器紧紧绑在一起。而在Java Collection的应用中,提供的具体迭代器角色是定义在容器角色中的内部类,这样便保护了容器的封装。同时容器也提供遍历算法接口,用户可以扩展自己的迭代器。
容器角色,以List为例。它也仅仅是一个接口,不罗列具体容器角色,是实现了List接口的ArrayList等类。这里只介绍和迭代器相关的内容,具体迭代器角色是以内部类的形式表现出来的。AbstractList是为了将各个具体容器角色的公共部分提取出来而存在的。设计代码如下:
//迭代器角色,仅仅定义了遍历接口
public interface Iterator{
//声明方法
boolean hasNext();
Object next();
void remove();
}
public abstract class AbstractList implements List{
······
//这个便是负责创建具体迭代器角色的工厂方法
public Iterator iterator(){
return new Itr();
}
}
//作为内部类的具体迭代器角色
private class Itr implements Iterator{ //实现接口
//创建成员变量
int cursor=0;
int lastRet=-1;
int expectedModCount=modCount;
public boolean hasNext(){
return cursor!=size();
}
public Object next(){
checkForComodification();
try{
Object next=get(cursor);
lastRet=cursor++;
return next;
}catch(IndexOutOfBoundsException e){
checkForComodification():
throw new NoSuchElementException():
}
}
public void remove(){
if(lastRet==-1)
throw new IllegalStateException();
checkForComodification();
try{
AbstractList.this.remove(lastRet);
if(lastRet<cursor)
cursor--;
lastRet=-1;
expectedModeCount=modCount;
}catch(IndexOutofBoundsException e){
throw new ConcurrentModificationException();
}
}
//设计方法checkForComodification()
final void checkForComodification(){
if(modCount!=expectedModCount)
throw new ConcurrentModificationException();
}
}
至于迭代器模式的使用,要先得到具体容器角色,然后再通过具体容器角色,得到具体迭代器角色。这样就可以使用具体迭代器角色来遍历容器。上述代码不是完整程序,只是演示各个角色的创建过程。
迭代器模式给容器的应用带来的好处:
(1)支持以不同的方式遍历一个容器角色,根据实现方式的不同,效果上会有差别。
(2)简化了容器的接口,但是在java Collection中为了提高可扩展性,容器还是提供了遍历的接口。
(3)对同一个容器对象,可以同时进行多个遍历,因为遍历状态保存在每一个迭代器对象中。
迭代器模式的适用范围:
(1)访问一个容器对象的内容而无须暴露它的内部表示。
(2)支持对容器对象的多种遍历。
(3)为遍历不同的容器结构提供一个统一的接口(多态迭代)。
迭代器的出现,避免了在数据结构中频繁的操作数据,同时也避免了代码的复杂性,为程序员提供了方便。在实际开发中,会使用到迭代器,方便进行查询、添加、删除元素等操作。迭代器模式在现实的应用中很广泛,特别是在以后的数据库程序开发中被广泛应用。
常见疑难解答:
(1)Collection集合接口与Collections集合类的区别:
Collections是java.util下的类,它包含各种有关集合操作的静态方法。
Collection是java.util下的接口,它是各种集合结构的父接口。
*List、Set继承自Collection接口,Map不继承Collection接口。
(2)ArrayList数组列表类和Vector存储类的区别:
同步性:Vector是线程安全的,是同步的。而ArrayList是线程不安全的,不是同步的。
数据增长:当需要增长时,Vector默认增长为原来一倍,而ArrayList却是原来的一半。
(3)HashMap散列映射和Hashtable散列表的区别:
二者都属于Map接口的类,作用都是将唯一键映射到特定的值上。
而HashMap类没有分类或排序,它允许一个null键和多个null键。
而Hashtable类似于HashMap,但是不允许有null键和null值,它也比HashMap慢,因为它是同步的。
(4)数据结构的种类有哪些:
数据结构分为两大类:线性数据结构和非线性数据结构。线性数据结构包括:线性表、栈、队列、串、数组和文件;非线性数据结构包括:树、图。
(5)List接口和Set接口的区别:
List:是一个有序的集合,可以包含重复的元素,提供了按索引访问的方式。这里的有序就是指有顺序的排放,并不是排序。
Set:从Collection接口继承而来,但没有提供新的抽象的实现方法,其中不能包含重复元素。