List接口对Collection进行了简单的扩充,它的具体实现类常用的有ArrayList和LinkedList,ArrayList是一种基与数组的形式进行存储的,因此它的随机访问速度极快;而LinkedList的内部实现是链表,它适合于在链表中间需要频繁进行插入和删除操作。
1,ArrayList从其命名中可以看出它是一种基与数组,数组属于线性表的结构:
package com.dao;
import com.entity.Student;
public class StudentManager {
private Student[] students;
//当前容量
private int capacity;
public StudentManager(int size)
{
students=new Student[size];
}
/**
* 在数组最后插入学生
* @param stu
*/
public void insert(Student stu)
{
//TODO
if(capacity>students.length){
System.out.println("数组空间已满");
}
students[capacity]=stu;
capacity++;
}
/**
* 在指定位置插入学生
* @param stu
* @param index
*/
public void insert(Student stu,int index)
{
//TODO
if(index<0 || index>capacity){
System.out.println("请求插入的位置有误");
}
else if(capacity>students.length){
System.out.println("不能在进行添加");
}
//指定位置的下标依次向后移动
for(int i=capacity;i>index;i--){
students[i]=students[i-1];
}
//循环玩后把空下的index插入数据
students[index]=stu;
capacity++;
}
/**
* 删除指定位置的学生
* @param index
*/
public void delete(int index)
{
//TODO
if(index<0 || index>capacity){
System.out.println("请求插入的位置有误");
}
else if(capacity>students.length){
System.out.println("不能在进行添加");
}
//首先capacity对应下标-1,students[i+1]为了去到最后一个值需要在-1,所以-2
for(int i=index; i<capacity-2;i++){
students[i]=students[i+1];
}
capacity--;
}
/**
* 遍历显示所有学生
*/
public void showAll()
{
//TODO
for(int i=0;i<capacity;i++){
System.out.println(students[i].toString());
}
}
/**
* 获取指定位置的学生
* @param index
* @return
*/
public Student getStudent(int index)
{
//TODO
if(index<0 || index>capacity){
System.out.println("请求插入的位置有误");
}
System.out.println(students[index].toString());
return students[index];
}
}
每次去指定位置删除,或者指定位置插入就相当费劲,要制定位置后面的数据要么整体前移,要么整体后移,严重影响性能;而在遍历、末尾插入数据和获取指定位置数据的效率还是蛮高的;
2,LinkedList的内部实现是链表,链表的数据结构:
package com.dao;
import com.entity.LinkNode;
public class Node {
/**
* 删除相应的节点
* @param length 指定链表长度
* @param node 指定删除的节点
*/
public LinkNode head;
public int length;
/**
* 删除节点
* @param node 删除节点的指定位置
*/
public void delNode(int node){
if(node<1)
{
System.out.println("删除的索引超出范围");
return;
}
//删除的是头节点
if(node==1)
{
head=head.next;
}
else//删除中间的节点
{
//找到删除位置的前一个节点
int tempIndex=1;
LinkNode curNode=head;
while(tempIndex<node-1)
{
tempIndex++;
//node-1了所以curNode.next刚好是指定节点
curNode=curNode.next;
}
//将制定位置的下一个节点做为头节点,head变为了curNode.next.next
LinkNode newhead=curNode.next;
head=newhead.next;
//删除下一个节点
curNode.next=curNode.next.next;
}
length--;
}
/**
* 形成循环链
* @param length 元素的长度
*/
public void add(int length){
LinkNode linknode =new LinkNode();
if(length==0){
System.out.println("输入的值有误");
}
//将 只足一家到指定节点的莫节点
for(int i=0; i<length;i++){
linknode.men=i+1;
//在莫节点添加一个节点
insert(linknode.men);
System.out.println(linknode.men);
}
//找到莫节点将其接到头结点形成循环链
LinkNode curNode=head;
while(curNode.next!=null)
{
curNode=curNode.next;
}
curNode.next=head;
}
/**
* 显示遍历
*/
public void display() {
if (null == head) {
System.out.println("链表没有元素");
} else {
System.out.println("--------------链表中所有元素为:-----------");
// 从头节点开始,一次找到下一个节点,显示节点的数据
LinkNode curNode = head;
while (null != curNode) {
System.out.println("没有被删除的是:"+curNode.men);
curNode = curNode.next;
if(curNode==head){
break;
}
}
System.out.println("链表的长度是:" + length);
}
}
/**
* 插入单个节点到莫节点
* @param obj 节点值
*/
public void insert(Object obj) {
// 定义要插入的节点对象
LinkNode newNode = new LinkNode();
newNode.men = obj;
// 链表中没有任何节点
if (null == head) {
head = newNode;
} else// 链表中已有节点
{
// 找到链表的末尾节点
LinkNode curNode = head;
// 循环完后,curNode就是末尾节点
while (curNode.next != null) {
curNode = curNode.next;
}
// 末尾节点的下一个节点是要插入的新节点
curNode.next = newNode;
}
length++;// 插入元素后链表长度+1
}
}
而链的使用则是以头节点为线索(数组可以用索引访问),删除指定位置节点只需要找到相应的头节点,然后把删除位置的下一个头节点做为删除位置上一个节点的后继(相比数组减少了移动的操作);在添加数据到末尾的时候的时候也是根据头节点从开始到结束(相比之下这里比数组在末尾添加的效率会低一些),由此也就可以看出ArrayList和LinkList的适合场景。
3,Vector和Stack
Vector和Stack也属于List的子类,vector相对于ArrayList是线程同步的,安全性更高(也就意味的效率会低于ArrayList),ArrayList当然容量需要增长的时候增加原来长度的一半,而Vector是增加以前的一倍;而Stack是继承Vector,对Vector进行了扩展;Stack也是一种特殊的线性表,里面的元素具有后进先出(先进后出)的特点。Stack有栈顶(最后进入栈的元素)和栈底(最先进入栈的元素),分顺序栈和链栈
a 顺序栈
package com.test.entity;
public class SeqStack {
public Object[] nodes;
public int length;//元素个数(长度)
private int increment;//扩展的空间大小
/**
* 构造方法
* @param initSize 顺序表原空间大小
* @param increment 自动扩展的空间大小
*/
public SeqStack(int initSize, int increment)
{
nodes=new Object[initSize];
this.increment=increment;
}
public void pushStack(Object obj)
{
if(length==nodes.length)
{
System.out.println("自动扩展空间大小");
//创建新的数组(比原数组大)
Object[] newNodes=new Object[nodes.length+increment];
//将原数组中的所有元素拷贝到新数组中
System.arraycopy(nodes, 0, newNodes, 0, nodes.length);
//用新数组替代原数组
nodes=newNodes;
}
nodes[length]=obj;
System.out.println("进栈的元素是:"+obj);
length++;
}
/**
* 出栈
*/
public void popStack()
{
if(!isEmpty())
{
System.out.println("出栈的元素:"+nodes[length-1]);
nodes[length-1]=null;
length--;
}
}
public boolean isEmpty()
{
return length==0;
}
/**
* 获取栈顶元素
* @return
*/
public Object getTop()
{
if(!isEmpty())
{
return nodes[length-1];
}
else
{
return null;
}
}
}
b. 链栈(把栈顶元素设置为头节点时进出栈效率高)
package com.test.entity;
public class LinkStack {
public LinkNode head;//头节点
public int length;
/**
* 进栈
* @param obj 进栈数据
*/
public void pushStack(Object obj)
{
LinkNode newNode=new LinkNode();
newNode.data=obj;
//头节点单独处理
if(null == head)
{
head=newNode;
}
else
{
newNode.next=head;
head=newNode;
}
System.out.println("进栈元素是:"+obj);
length++;
}
/**
* 出栈
*/
public void popStack()
{
if(!isEmpty())
{
System.out.println("出栈的元素是:"+head.data);
head=head.next;
length--;
}
}
public boolean isEmpty()
{
return length==0;
}
/**
* 获取栈顶元素
* @return
*/
public Object getTop()
{
if(!isEmpty())
{
return head.data;
}
else
{
return null;
}
}
}
二,队列
队列:一种线性表,里面存储的元素具有先进先出的规则。也分顺序队列和链表队列
1,顺序队列:
package com.test.entity;
public class SeqQueue {
public Object[] data;
public int maxSize;// 队列的最大长度,也即数组的空间大小
public int first;// 队头下标
public int last;// 队尾下标
public int length;// 队列元素个数
public SeqQueue(int maxSize) {
this.maxSize = maxSize;
data = new Object[maxSize];
}
/**
* 入队
*
* @param obj 入队的数据
*/
public void insertLast(Object obj) {
if (length == data.length) {
System.out.println("队列已满!");
} else {
System.out.println("入队:" + obj);
// 当前入队的元素
data[last] = obj;
// 下一次入队的下标
last = (last + 1) % maxSize;
length++;
}
}
/**
* 出队
*/
public void deleteFirst() {
if (length == 0) {
System.out.println("队列为空!");
} else {
System.out.println("出队:" + data[first]);
// 当前的对头元素删除
data[first] = null;
// 下一个要出队的元素下标
first = (first + 1) % maxSize;
length--;
}
}
/**
* 按出队的顺序遍历
*/
public void display() {
if (length == 0) {
System.out.println("队列已空");
} else {
System.out.println("队列的长度:" + length);
for (int i = 0; i < length; i++) {
System.out.println(data[(first + i) % maxSize]);
}
}
}
/**
* 出队所有元素
*/
public void deleteAll() {
while (length > 0) {
deleteFirst();
}
}
}
2,链表队列:
package com.test.entity;
public class LinkQueue {
public LinkNode first;//队头节点
public LinkNode last;//队尾节点
public int length;//链表长度
/**
* 入队
* @param obj 数据
*/
public void insertLast(Object obj)
{
LinkNode newNode=new LinkNode();
newNode.data=obj;
System.out.println("入队的元素:"+obj);
//头节点单独处理
if(null == first)
{
first=newNode;
last=newNode;
}
else
{
//新入元素排到原队尾的下一个
last.next=newNode;
//产生新的队尾
last=newNode;
}
length++;
}
/**
* 出队
*/
public void deleteFirst()
{
if(null==first)
{
System.out.println("队列为空!");
}
else
{
System.out.println("出队的元素:"+first.data);
first=first.next;
length--;
}
}
/**
* 遍历
*/
public void display()
{
if(null == first)
{
System.out.println("队列为空!");
}
else
{
System.out.println("队列的长度:"+length);
//从头节点开始,依次后移遍历
LinkNode curNode=first;
while(null != curNode)
{
System.out.println(curNode.data);
curNode=curNode.next;
}
}
}
/**
* 所有元素出队
*/
public void deleteAll()
{
while(length>0)
{
deleteFirst();
}
}
}
三,Set
1 Java.util.HashSet类实现了Java.util.Set接口。
它不允许出现重复元素;
不保证和政集合中元素的顺序
允许包含值为null的元素,但最多只能有一个null元素。
2,TreeSet
TreeSet描述的是Set的一种变体——可以实现排序等功能的集合,它在讲对象元素添加到集合中时会自动按照某种比较规则将其插入到有序的对象序列中,此时就会想到树的结构,树:一种由节点构成的非线性结构(子树
节点分类:根节点、分支节点、叶子节点
节点的度:一个节点的子树个数
树的度:树的所有节点中,节点的度的最大值
树的深度:树的层数
父节点和子节点:在子树中,由一个节点分出的节点叫它的子节点,分出子节点的节点叫父节点)
二、二叉树
二叉树:树的度不超过2的叫二叉树
基本形态(5种):
a. 空树
b. 只有根节点
c. 只有左子树
d. 只有右子树
e. 左右子树都有
特殊形态(2种):
a. 满二叉树 树的每层节点都挂满
一层的节点数=2的(层数-1)次方
树的节点数=2的深度次方 - 1
b. 完全二叉树:每个节点的编号和满二叉树一致
规律:
二叉树每层的节点数<=2的(层数-1)次方
二叉树所有的节点数<=2的深度次方 - 1
package com.test.entity;
import java.util.Scanner;
public class LinkTree {
private Scanner sc = new Scanner(System.in);
public TreeNode createTree() {
System.out.println("请输入节点的数据:");
char ch = sc.nextLine().charAt(0);
TreeNode node = null;
// 产生本子树的父节点(当输入的数据为空时返回)
if (ch != '#') {
node = new TreeNode();
node.data = ch;
System.out.println("产生新节点:" + ch);
System.out.println("产生" + ch + "的左子树");
// 生成左孩子节点(调用本方法)
node.leftChild = createTree();
System.out.println("产生" + ch + "的右子树");
// 生成右孩子节点(调用本方法)
node.rightChild = createTree();
}
// 返回生成的本子树
return node;
}
/**
* 前序遍历
* @param node 根节点
*/
public void preOrder(TreeNode node) {
if (null != node) {
// 访问根节点
System.out.println(node.data);
//访问左子树
preOrder(node.leftChild);
//访问右子树
preOrder(node.rightChild);
}
}
/**
* 中序遍历
* @param node 根节点
*/
public void inOrder(TreeNode node)
{
if(null != node)
{
inOrder(node.leftChild);
System.out.println(node.data);
inOrder(node.rightChild);
}
}
/**
* 后序遍历
* @param node 根节点
*/
public void postOrder(TreeNode node)
{
if(null != node)
{
inOrder(node.leftChild);
inOrder(node.rightChild);
System.out.println(node.data);
}
}
}
Map没有继承Collection接口
1,Hashtable类
Hashtable继承Map接口,实现一个key-value映射的哈希表。任何非空(non-null)的对象都可作为key或者value。
2,HashMap类
HashMap和Hashtable类似,不同之处在于HashMap是非同步的,并且允许null,即null value和null key
3,WeakHashMap类
WeakHashMap是一种改进的HashMap,它对key实行“弱引用”,如果一个key不再被外部所引用,那么该key可以被GC回收。