Java没有指针,只有引用,如何关联变量,实现各类数据结构?
C语言离不开指针,指针作为一种派生类型,是数据关联的一种主要手段(实现顺序存储和链式存储)。数组下标运算也只是指针算术运算的语法糖。其中数组名是一个常量(为安全考量),数组下标运算替换为指针运算,由C编译器完成。
C++语言针对指针的复杂性和安全性问题,引入了引用类型,引用类型是一种特殊的指针,是一种常量类型,在不同的上下文中可以显式使用引用的地址属性或值属性。
C++也不能没有指针类型(没法实现链式存储),除非引用类型是一种非常量,但这又违背了引入引用的初衷,C++是通过原生指针、引用、智能指针、原生数组、vector等语法机制来实现相互补充的。
Java舍弃了指针类型,其引用类型也可以是一个变量(可使用操赋值运算符,但不支持算术运算),因为Java用GC机制,C++的动态内存泄漏问题在Java这里不是问题。数组也是一种引用类型。对于Java类型来说,分为两大类型,一种是基本类型,一种是引用类型。非基本类型(复合类型)全部统一建立在堆上,这块堆内存的首地址保存栈上。当然,引用的底层也是指针,但这只是编译器层面的问题。
Java作为试图改良C++的编程语言,一定程度上摒弃了C++的复杂性,其中就包括指针,虽然也有引用类型,但是一个与C++不同的引用类型,Java的引用也是一个特殊的指针(指针是显式的地址使用),但不是一个常量,可以用做左值,没有C++考虑让引用作为常量的原因(安全问题)的问题吗,没有,因为其有自动GC(Garbage collection 垃圾回收)功能,且所有对象都是通过一个引用变量来操作,对象本身生存在堆内存上,引用本身生存在栈内存上,由GC根据对象引用的情况来确定对象回收的时间。
JAVA中的对象类型本质上应该叫做对象指针类型。那么传统的对象类型呢?在JAVA里已经不见了踪影!既然没有了传统的对象类型,那么对象引用变量前面的*也就可以不要了。对象引用变量也就可以简称为对象变量了,反正也不会和其它概念混淆! 所有的对象变量都是引用,没有非引用的对象变量,想不用引用都不行,这就是指针的泛化和强化。 不叫指针了,就叫对象变量,这就是概念上的淡化和弱化。 没有了指针的加减运算,也没有了解引用*、对象指针成员引用->等运算符,这是对指针的简单化。
引用传参时,在函数体内是隐式取值还是取址形成副作用,取决于上下文。
我们知道,对于数据结构而言,非线性结构其实质是一个二维的线性结构。例如,图这种数据结构的存储结构可以是邻接矩阵或邻接表。线性表的存储结构主要是使用顺序存储和链式存储。
我们知道,在C++中,数组下标操作符[]是对应指针运算符的语法糖,对于Java来说,没了指针,也没有指针的加减运算,只是限制到了下标操作符[]而已。
Java的数组是动态数组,存储在堆上。数组名本身是一个栈变量,存储建立数组的堆内存的地址。另外,Java的数组提供了length属性。
指针及其算术运算在循环操作中有一定优势,而Java通过一定的语法机制来实现相同效果。
一些容器提供了size()和isEmpty()方法,在循环方法,也提供了以下一些语法机制的支持:
利用for范围迭代(底层也是迭代器实现)
迭代器iterator.hasNext()
for each循环
对于链式存储,不需要指针的加减运算,对于C++和Java(其引用支持赋值运算)而,其实现是一致的,对于结点类而言,都是使用了一个地址域(指针或引用)来联结:
import java.util.Scanner;
import java.util.Random;
class Node{
private Node next;
private int value;
public Node ( int val ){
value = val;
next = null;
}
public int getValue() { return value; }
public Node getNext() { return next; }
public void setValue( int val ) { value = val; }
public void setNext( Node nxt ) { next = nxt; }
public String toString() { return value + ", "; }}class LinkedList{
private Node headPtr = null;
// The constructor creates an empty list
public LinkedList() {
headPtr = null;
}
// Determine if the List is empty
public boolean isEmpty(){
return headPtr == null;
// future work
}
// Insert one Node containing data at the head
// of the list. This will be explained in a few pages.
public void insertFirst( int data ){
Node newFirst = new Node( data );
newFirst.setNext( headPtr );
headPtr = newFirst;
}
public void deleteFirst(){
if( headPtr != null )
{
headPtr = headPtr.getNext(); // headPtr now points at second node, or
// is null if there was no second node.
}
}
// Traverse the list, printing each node
public void traverse() {
Node p = headPtr;
while( p != null )
{
System.out.print( p );
p = p.getNext();
}
}
}
public class Main{
public static void main( String[] args )
{
LinkedList list = new LinkedList();
Scanner scan = new Scanner( System.in );
Random rand = new Random();
System.out.print("How many nodes? ");
int numNodes = scan.nextInt();
for( int j = 0; j
-End-