I.数组
1.定义
严格来说,数组并不是本章所介绍的库类。它更接近于C语言当中的数组概念(采用指针来指向一连串处于一个集合内的多个同类型的元素),直接采用类型-变量名语句完成定义。
char a[];
char []b;
特别指出:
char []a,b,c;
char a[],b[],c[];
char []a,[]b,[]c;
这三个语句是等价的。
允许在定义之初初始化赋值:
char []c={'a','b','c','d','e'};
等价于:
char c[]=new char(5);
char c[0]='a';
char c[1]='b';
char c[2]='c';
char c[3]='d';
char c[4]='e';
java的数组下标从零开始。
2.创建实例对象
与C语言不同的是,定义数组并不需要声明空间,仅在建立数组对象时必须采用构造方法声明空间。在C语言当中:
char a[10];
char *b;
b=(char *)malloc(10*sizeof(char));
这样的语句才能有效地定义出数组。而Java中:
char c=new char(5);
Java中不必声明空间,但是声明并初始化之后空间也就分配完毕,不能使用或访问其他的空间:
正确示例:
char a[]= {'a','b','c','d'};
System.out.println(a[3]);
错误示例:
char a[]= {'a','b','c','d'};
a[4]='e';
System.out.println(a[4]);
报错为:
Exception in thread “main” java.lang.ArrayIndexOutOfBoundsException: 4 at whatever.w.main(w.java:7)
仅仅赋值或者调用都会报错。
3.引用数据类型数组
当数组元素的数据类型是引用数据类型(类,接口,数组类型),a[i]表示一个引用。尚未赋值时,默认值为null。
String s[]=new String[3];
s[0]=new String("abc");
其中的s[0]就是一个对String类的引用。也可以:
String s[]= {new String("abc"),new String("def"),new String("ghi")};
4.多维数组
1.创建多维数组对象的方法
(1)直接创建多维数组对象
int [][]matrix=new int[2][2];
(2)从高维开始逐渐创建数组对象
比如一个存储器的锁存器阵列为256* 128* 8,我们要定义一个三维数组表示之。
int [][][]matrix;
matrix=new int[256][][];
for(int i=0;i<256;i++)matrix[i]=new int[128][];//matrix有256个元素,每个元素matrix[i]有128个元素
for(int i=0;i<256;i++)
for(int j=0;j<128;j++)matrix[i][j]=new int[8];//每个matrix[i][j]都有八个整型元素
高维数组的各个元素都是数组,且元素个数未必要一样。
(3)数组初始化语句
int [][][]matrix= {{{1,2},{3,4}},{{5,6},{7,8}},{{9,10},{11,12}}};
在这里,有:
matrix[0][0][0]=1;
matrix[0][0][1]=2;
matrix[0][1][0]=3;
matrix[0][1][1]=4;
matrix[1][0][0]=5;
matrix[1][0][1]=6;
matrix[1][1][0]=7;
matrix[1][1][1]=8;
matrix[2][0][0]=9;
matrix[2][0][1]=10;
matrix[2][1][0]=11;
matrix[2][1][1]=12;
2.多维数组引用实例:三阶幻方
三阶幻方是将1-9这些数字填入九宫格的游戏,要求九个位置上的数字满足每行,每列,每条对角线上的三个数字都是相等的(可见等于15)。
这里给出一设计,不再详述内容:
package whatever;
public class Grid {
int board[][];
public Grid()
{
board=new int[3][3];
}
public void outputGrid()
{
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
System.out.print(board[i][j]+" ");
}
System.out.print("\n");
}
}
public void initGrid()
{
int add=1;
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
board[i][j]=add;
add++;
}
}
}
public boolean isLastData()
{
int check=9;
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
if(board[i][j]!=check)return false;
check--;
}
}
return true;
}
public void nextData()
{
for(int i=board.length-1;i>=0;i--)
{
for(int j=board[i].length-1;j>=0;j--)
{
if(board[i][j]==9)board[i][j]=1;
else
{
board[i][j]++;
return;
}
}
}
}
public boolean isDuplicate()
{
int digit[]=new int[10];
for(int k=0;k<=9;k++)
{
digit[k]=0;
}
for(int i=0;i<=2;i++)
{
for(int j=0;j<=2;j++)
{
digit[board[i][j]]+=1;
}
}
for(int t=1;t<=9;t++)
{
if(digit[t]!=1)return true;
}
return false;
}
public boolean isAnwser()
{
if(this.isDuplicate())return false;
boolean r=board[0][0]+board[1][1]+board[2][2]==15;
r=r&&(board[0][2]+board[1][1]+board[2][0]==15);
r=r&&(board[0][0]+board[0][1]+board[0][2]==15);
r=r&&(board[1][0]+board[1][1]+board[1][2]==15);
r=r&&(board[2][0]+board[2][1]+board[2][2]==15);
r=r&&(board[0][0]+board[1][0]+board[2][0]==15);
r=r&&(board[0][1]+board[1][1]+board[2][1]==15);
r=r&&(board[0][2]+board[1][2]+board[2][2]==15);
return r;
}
public void arrange()
{
for(this.initGrid();!this.isLastData();this.nextData())
{
if(this.isAnwser())
{
outputGrid();
System.out.print("\n");
}
}
}
public static void main(String []args)
{
Grid m=new Grid();
m.arrange();
}
}
II.Scanner&Java输入方法
在学习Java之初我们就编写了HelloWorld.java程序,知道了Java基本的输出方法。后来经过类的学习,我们也知道了System.out.println()语句的具体意义(System包下的out类下的println方法)
那么问题来了。到现在为止我们还不知道如何进行输入,以至于最简单的算法类问题都无法实践。
看一段程序:
package whatever;
import java.util.Scanner;
public class w {
public static void main(String args[])
{
Scanner sc=new Scanner(System.in);
int a=sc.nextInt();
System.out.println(a);
}
}
这个程序直接实现了C语言当中scanf()语句的功能。
详细的原理后面会进一步介绍,在学习java初期,可以背诵这段代码(宛若一段能够造就输入魔法的咒文)
III.字符串,字符串缓冲区
1.String的构造方法
1.字符串直接量
“abcdef”就是一个字符串对象。比如:
String a="abcdef";
2.类String自构造方法
如其他类,String也具有自身的构造方法String()。
比如:public String()
String s=new String();
System.out.print(s);
这个代码是合法的,且不会输出任何。不调用任何参数的String()将会构造一个内容为"\0"的字符串,不同于:
String s=null;
表示s变量不指向任何一个String实例。
**public String(byte[] bytes)**可以依照bytes[]数组当中各个整型数依次对应ASCII码的字符组成字符串实例对象。比如:
byte b[]= {75,76,77,78,79};
String b1=new String(b);
System.out.print(b1);
输出:
KLMNO
public String(char[] value) 可以集中char数组value中的各个字符成为一个字符串实例对象,如:
char b[]= {'a','b','c'};
String b1=new String(b);
System.out.print(b1);
public String(String original) 生成一个和original字符串一模一样的字符串实例对象,如:
String a="abcdef";
String b1=new String(a);
System.out.print(b1);
3.类String成员方法构造实例对象
public static String valueOf()其多态地应对多种参数,效果是将这些参数转化成字符串再返回成一个字符串实例。
String.valueOf(12)所得实例对象相当于“12”
String.valueOf(true)所得实例对象相当于“true”
对于某个类object 的变量ob,
String.valueOf(ob)所得实例对象相当于ob.toString()。
public String toString()被隐式地加入任何类的成员方法列表,能强制将这个类的变量转化为字符串。当使用System.out.print()输出并不是字符串的变量时,系统自动先对参数调用toString(),使得输出为字符串。
其余的一些成员方法也能构造字符串实例,暂且罗列在下面的重要操作成员方法当中。
4.采用+
形如:
String a="123"+"abc";
其中+一侧倘若并非String实例,先调用toString()。
2.String的重要操作成员方法
1.public String concat(String str)----拼接
String a="abcdef";
String b1=a.concat("ghi");
System.out.print(b1);
输出:
abcdefghi
当str指向null,返回原实例this。
2.public String replace(char oldChar,char newChar)----替换指定字符
String a="abcabc";
String b1=a.replace('a','d');
System.out.print(b1);
输出:
dbcdbc
若oldChar在this实例中不存在,直接返回原实例。
3.public String toLowerCase()----字符全部小写
4.public String toUpperCase()----字符全部大写
5.public String trim()----清去首尾空白符
其中,全是空格符的字符串返回空字符串实例“\0”
6.public String substring(int beginIndex)----取从beginIndex+1到末尾的子串
7.public String substring(int beginIndex,int endIndex)----取从beginIndex+1到endIndex的子串
8.public static String format(String format,object…args)----创建格式化字符串并返回其引用
String format参数是格式字符串,其形式为:
% 参数索引$ 宽度 .精度 变换类型
参数索引$ 项不是必要项,缺省为 1$ ,表达采用参数表当中第1个或者第x个参数进行目标格式化。
比如format("%2$B",12,‘a’,6);目标格式化对象就是第二个参数‘a’。
宽度.精度 也不是必要项,可以仅有宽度,也可仅有.精度。宽度表达最少应当有多少个字符;
变换类型是必要项,指示按照格式将参数索引所指向的参数转化为何种类型。
String b1=String.format("%1$d",12,'a',6);
System.out.print(b1);
输出
12
若
String b1=String.format("%2$d",12,'a',6);
System.out.print(b1);
则报错。此处的格式化并不会自行格式转换。
String b1=String.format("%3$3.5f",12,'a',125.74);
System.out.print(b1);
输出:
125.74000
若
String b1=String.format("%3$3.3f",12,'a',125.74275);
System.out.print(b1);
输出:
125.743
(智能四舍五入)
若
String b1=String.format("%3$10.3f",12,'a',125.74275);
System.out.print(b1);
输出:
125.743
也就是说,宽度不足时在前端补齐空格,待输出对象宽度若达到,不作处理
常用的变换类型:
‘b’:若对应参数为(引用数据类型)null,比如(String)null,或者参数就是null,输出false;若对应参数为boolean类值,输出对应值对应字符串(false/true);除此以外均输出true。
String b1=String.format("%1$ b",12);------true
String b1=String.format("%1$ b",‘a’);------true
String bnull=null; String b1=String.format("%1$ b",bnull);------false
String bnull=new String(); String b1=String.format("%1$ b",bnull);------true 同上面的12与‘a’
‘B’:将上结果大写
‘c’:对应参数转化为Unicode字符,整型数对照ASCII表
String b1=String.format("%1$ c",71);------G
‘C’:将上述结果大写
‘d’:转为十进制整数
String b1=String.format("%1$ d",‘A’);------报错
‘o’:转为八进制整数
String b1=String.format("%1$ o",79);------117
‘x’:转为十六进制整数
String b1=String.format("%1$ x",79);------4f
‘X’
‘e’:规格浮点化
String b1=String.format("%1$ e",12.3);------1.230000e+01
‘f’:String b1=String.format("%1$f",12.3);------12.300000
9.public int length()----取长度
int i="avc".length();
System.out.print(i);
输出
3
10.public boolean isEmpty()------判空
11.public char charAt(int index)------返回序列中第index+1个字符
12.public int indexOf(int ch)------返回ch指示的字符在当前字符串中第一次出现的索引值
int i="avc".indexOf(97);
System.out.print(i);
输出
0
若:
int i="avc".indexOf('c');
System.out.print(i);
输出:
2
若不存在,返回-1
13.public int indexOf(int ch,int fromIndex)------ch所指示字符在fromIndex所示位置之后第一次出现的索引
int i="aaaaaaa".indexOf(97,4);
System.out.print(i);
输出
4
14.public int indexOf(String str,int fromIndex)------str字符串在this字符串中在fromIndex所示位置之后第一次出现的索引;public int indexOf(String str)
int i="abcabcabcabc".indexOf("abc",7);
System.out.print(i);
输出
9
若
int i="abcabcabcabc".indexOf("abc",6);
System.out.print(i);
输出
6
15.public int lastIndexOf(int ch,int fromIndex)
public int lastIndexOf(String str)
public int lastIndexOf(String str,int fromIndex)------最大索引,不存在返回-1
16.public int compareTo(String anotherString)
一般地,返回this字符串和anotherString字符串第一个不同的字符的Unicode值的差
若其中一个恰好为另一个的子串,返回this.length()-anotherString.length()
17.public int compareToIngnoreCase(String anotherString)-----同上,忽略大小写
18.public boolean equals(Object anObject)
当且仅当anObject指向的字符串与this字符序列相等返回true
19.public String intern()
程序中,字符串直接量、+连接的字符串结果、以及此方法构造的每个字符串都唯一互异地存放在字符串池当中。
如果 this已经在池中,返回这个字符串的引用;若池中不存在this相同的实例对象,则在池中添加此实例对象并返回其引用。
s1.intern()==s2.intern()可以用于判断s1,s2是否指向同一个实例对象。
String s1="abc";
String s2=s1.intern();
if(s1.intern()==s2.intern())System.out.print(s1+s2);
输出
abcabc
3.StringBuffer 字符串缓冲区
字符串实例一旦构造无法进行更改,只能对变量进行另一个实例的赋值。而字符串缓冲区则具有可修改性。缓冲区能够存放的字符数称为容量capacity,包含的字符数为length长度。
IV.向量Vector
Java Application Programming Interface(API)称为Java应用程序编程接口。作为接口,Java API是一系列规定好的类(在其他语言当中或体现为函数),提供应用程序与开发人员基于某软件或硬件得以访问一组例程的能力,而又无需访问源码或理解内部工作机制。
在一个平台能够支持Java程序以前,必须在这个特定平台上明确地 实现 API的功能,从而Java跨平台运行的性能才能得到保证。
Java Collection API 集合框架类
Collection也是一系列规定好的类,它表示离散数学理论上的集合。其中Set是具有无序特点的表,List是顺序表,Map是映射。
collection用于存储一系列的对象,其存储具有长度不固定的特点。
Vector向量类包含在java.util.Vector。
向量是数组功能上的升级,因为数组在创建之初就决定了数组对象的长度,不可以再更改;而向量同样存储对象,却可以在创建之后再添加元素。
向量对象的存储空间大小为向量对象的容量,元素个数为向量对象的长度。当向量对象的容量不足以容纳长度,容量会自动扩大。扩大后的新容量=max{旧容量+容量增量,向量对象的新长度}
1.声明和构造向量
Vector<String> vs=new Vector<String>();
Vector尽管比之数组无需在使用之初定义长度,但仍需要指定向量的类型,从而让向量指向此类型的实例。如果写作:
Vector v2=new Vector();
会受到警告。
2.对向量实例对象的操作
1.public boolean add(E o);public void addElement(E obj);
将指定元素添加到向量对象末尾,使得向量对象长度增1.
2.public void add(int index,E element);public void insertElementAt(E obj,int index);
在index处插入新元素
3.public E set(int index,E element);public void setElementAt(E obj,int index);
用于修改index位置上的元素为element或obj。
import java.util.Vector;
public class w{
public static void main(String args[])
{
Vector<String> vs=new Vector<String>();
vs.add("adc");
vs.add(0,"top");
System.out.println(vs);
vs.set(0, "mid");
System.out.println(vs);
}
}
输出:
[top, adc]
[mid, adc]
4.public void clear();public void removeAllElements();------删除向量对象的全部元素
5.public E remove(int index);public void removeElementAt(int index);------删除index处元素
在刚刚的程序后面补上:
vs.remove(0);
System.out.println(vs);
新增的输出:
[adc]
6.public boolean remove(Object o);public boolean removeElement(Object obj)------删除第一个与o对象相等的元素
这里的相等采用public boolean equals(Object o)判断的,此成员方法属于Object类,故而继承给一切类。
7.public boolean contains(Object elem);------判断对象是否包含在向量对象元素中
Vector<String> vs=new Vector<String>();
vs.add("adc");
vs.add(0,"top");
System.out.println(vs);
vs.set(0, "mid");
System.out.println(vs);
if(vs.contains("adc"))System.out.println("true");
输出:
[top, adc]
[mid, adc]
true
8.public int indexOf(Object elem);public int indexOf(Object elem,int index);
返回elem对象在向量对象中第一次出现的索引,若不存在返回-1;后者只查找index位置后的第一次出现的索引。
public int lastIndexOf(Object elem);public int lastIndexOf(Object elem,int index);同理。
9.public E elementAt(int index);public E get(int index);------返回下标index所示元素的实例对象。
public E lastElement();同理。
10.public int capacity();------返回向量对象的容量。
11.public int size();------返回向量对象的长度。
12.public boolean isEmpty();------判空
13.public void trimToSize();------减小向量容量,使得长度等于容量节约空间
14.采用迭代器Iterator获取向量对象元素
public Iterator< E > iterator();此方法是Vector类的成员方法,返回当前向量对象所对应的迭代器iterator。
iterator就仿佛C语言中用for语言对数组操作所新建的循环变量int i。
初始化Iterator使用如下语句:
Vector<String> vs=new Vector<String>();
Iterator<String> i=vs.iterator();
表示新建i是vs的迭代器。初始化迭代器默认地将迭代器指向向量对象第一个元素的前一个位置。
java.util.Iterator 有两个重要成员方法:
boolean hasNext();若迭代器指示位置
E next();返回iterator指向的下一个向量对象的元素,并且将iterator后移一位。若下一个元素已经不存在则出现异常。
看一段代码:
import java.util.Vector;
import java.util.Iterator;
public class w{
public static void main(String args[])
{
Vector<String> vs=new Vector<String>();
vs.add("adc");
vs.add(0,"top");
vs.set(0, "mid");
vs.add("faker");
vs.add("Duke");
System.out.println(vs);
Iterator<String> i=vs.iterator();
while(i.hasNext())
{
System.out.println(i.next());
}
}
}
输出:
[mid, adc, faker, Duke]
mid
adc
faker
Duke
一个向量对象的迭代器并不一定唯一,再新建一个迭代器依然会指向向量对象第一个元素的前一位置。
V.映射Map
1.映射原理
JDK给出了多种Map类,它们在功能和实现原理上有着本质的不同。然而Map本身则是一个接口,并不是直接可以创建的类。
我们来看一段eclipse对java.util.Map的注释:
this interface takes the place of the Dictionary class, which was a
totally abstract class rather than an interface.
这个接口取代了Dictionary类,Dictionary类是一个完全抽象的类,而不是一个接口。
The Map interface provides three collection views, which allow a map’s contents to be
viewed as a set of keys, collection of values,or set of key-value
mappings. The order of a map is defined as the order in which the
iterators on the map’s collection views return their elements. Some
map implementations(like the TreeMap class) make specific guarantees
as to their order; others, like the HashMapclass, do not.
映射接口提供三个集合视图,允许将地图的内容视为一组键、值集合或一组键值映射。映射的顺序定义为映射的集合视图上的迭代器返回其关联的顺序。一些映射实现,如treemap类,对它们的顺序做出特定的保证;其他的,如hashmap类,则不这样做。
Note that this implementation is not synchronized.
请注意,此实现(HashMap)不支持同步机制
Map在java.util.Map的定义是为了让编程者能够通过继承接口创建自己的映射类。而几个典型的映射包括:TreeMap,HashMap.WeakHashMap.Hashtable。它们都称为Map的实现(即接口意义上的实现)
2.映射实现Hashtable,HashMap,WeakHashMap
如果系统学习过数据结构,对于哈希表的实现原理会特别熟悉。这三个实现都是基于散列函数原理的。
这三个类非常相近,所以我们先介绍他们共同的用法。
(1)构造映射实现实例
这三个类都是映射Map的实现,而我们首先来构造这三个类的实例,并完成其初始化。
HashMap<String,Integer> ms=new HashMap<String,Integer>();
Hashtable<String,Integer> ts=new Hashtable<String,Integer>();
WeakHashMap<String,Integer> ws=new WeakHashMap<String,Integer>();
也可以在括号里直接填入参数,声明哈希表容量和哈希表装填因子。
构造方法多态地有:
public Hashtable();
public Hashtable(int initialCapacity);填入的参数是哈希表容量
public Hashtable(int initialCapacity,float loadFactor);后者表示装填因子
和哈希表原理一致地,哈希表元素个数/哈希表容量=装填因子,装填因子的缺省值是0.75,容量的缺省值是11
比如:
int s=800;
HashMap<String,Integer> ms=new HashMap<String,Integer>(800*4/3,0.75f);
(2)建立映射联系
建立映射联系采用方法public V put(K key,V value),其中认定Map被定义为Map(K key,V value)。实质上映射就是反应从键到值关系(From Key to Value Relation)
import java.util.WeakHashMap;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
public class w{
public static void main(String args[])
{
HashMap<String,Integer> ms=new HashMap<String,Integer>();
Hashtable<String,Integer> ts=new Hashtable<String,Integer>();
WeakHashMap<String,Integer> ws=new WeakHashMap<String,Integer>();
String []sa= {"Lisa","Rock","California","NewYork"};
for(int i=1;i<=sa.length;i++)
{
ms.put(sa[i], i);
ts.put(sa[i], i);
ws.put(sa[i], i);
}
}
}
如上例,String []sa是一组键,1,2,3,4是一组值。这个例子和一般感受上是相反的(习惯认为简单的1,2,3,4是键),但不论是怎样形式的映射,都从接口Map处继承了put等一些重要的成员方法。这样就建立了映射联系,或者可以认为是给映射赋值。
(3)共享的重要成员方法
1.public V get(Object key)------按照键返回值
2.public void clear()------清空映射内容
3.public boolean containsKey(Object key);
public boolean containsValue(Object value);
public boolean contains(Object value)
上述三个类都用于判断此键、值对应的元素是否在Map当中存在。
4.public boolean isEmpty()------判空
5.public V remove(Object key)------删除键对应元素,返回对应value
6.public int size()------返回映射元素个数
(4)区别
一般翻译此三个类作:Hashtable哈希表,HashMap哈希映射,WeakHashMap弱哈希表
1.完整性
哈希表不允许关键字或值的任何一项填入null。而哈希映射,弱哈希表都允许。
2.同步机制
哈希表支持同步机制,这表明即便有多个线程同时访问哈希表类实例对象,其实例对象的操作结果正确性和数据正确性都是可以保证的。而另外两个映射实现的结果则是不确定的,这叫做不支持同步机制。这在后面的多线程编程内容中会详细介绍。
3.弱哈希表的特点
弱哈希表会自动按照一定的规则检查各个元素是否常用,若并不,则会自动地从实例对象的存储空间中回收。