在jdk api中专门设计了一组类,这组类的功能就是实现各种各样方式的数据存储,这样一组专门用来存储其它对象的类,一般被称为对象容器类,简称容器类,这组类和接口的设计结构也被统称为集合框架(collection framework)。
这组类和接口都包含在java.util包中。
为了使整个集合框架中的类便于使用,在设计集合框架时大量的使用接口,实际实现的功能类实现对应的接口,这样可以保证各个集合类的使用方式保持统一。
在集合框架中,提供的存储方式共有两种:
1、按照索引值操作数据
在这种存储方式中,为每个存储的数据设定一个索引值,存储在容器中的第一个元素索引值是0,第二个索引值是1,依次类推。在操作数据时按照索引值操作对应的数据,实现这种方式的集合类都实现java.util.collection接口。
2、按照名称操作数据
在这种存储方式中,为每个存储的数据设定一个名称(任意非null的对象都可以作为名称),以后按照该名称操作该数据,要求名称不能重复,每个名称对应唯一的一个值。这种存储数据的方式也称作名称-数值对,也就是名值对存储。实现这种方式的几个类都实现java.util.map接口。
这里“按照索引值操作数据”的存储方式,又按照容器内部是否能够存储重复的元素,划分成两类:
1、允许存储重复元素。
这种存储方式中,所有的类都实现了java.util.list接口。
2、不允许存储重复元素。
这种存储方式中,所有的类都实现了java.util.set接口。
这样,集合框架中的类就分成了三大类:
1、list系列
该系列中的类按照索引值来操作数据,允许存放重复的元素。
2、set系列
该系列中的类按照索引值来操作数据,不允许存放重复的元素。
3、map系列
该系列中的类按照名称来操作数据,名称不允许重复,值可以重复,一个名称对应一个唯一的值。
而 在数据结构中,实现数据的存储又可以使用不同的数据结构类型进行存储,例如数组、链表、栈、队列和树等,则以上三类集合框架可以使用不同的数据结构类进行 实现,使用每种数据结构则具备该中数据结构的特点。例如使用数组则访问速度快,使用链表则便于动态插入和删除等,这样就造成了集合框架的复杂性。
另外,在将对象存储到集合类中,为了加快存储的速度,要求被存储对象的类中必须覆盖equals方法和hashcode方法。
对于这些集合类,下面按照以上三个系列的顺序一一进行说明。
9.6.3.1 list系列
list系列的类均实现list接口,大部分的类都以list作为类名的后缀,也有部分该体系中的类命名比较特殊。
该系列中的类,比较常见的有arraylist和linkedlist两个。其中arraylist是以数组为基础实现的list,而linkedlist则是以链表为基础实现的list,arraylist拥有数组的优点,而linkedlist拥有链表的优点。
由于该体系中的类均实现list接口,所以在这些类的内部,相同的功能方法声明是保持一致的,下面进行一一介绍:
a、add方法
boolean add(object o)
该方法的作用是追加对象o到已有容器的末尾。
另外一个add方法:
void add(int index, object element)
该方法的作用是将对象element插入到容器中索引值为index的位置,原来位于该位置的对象以及后续的内容将依次向后移动。
b、addall方法
boolean addall(collection c)
该方法的作用是将容器对象c中的每个元素依次添加到当前容器的末尾。
另外一个addall方法:
boolean addall(int index, collection c)
该方法的作用是将容器对象c中的第一个元素插入到当前容器中索引值为index的位置,第二个元素插入到当前容器中索引值为index+1的位置,依次类推。而当前容器中原来位于index以及index索引值以后的元素则依次向后移动。
c、get方法
object get(int index)
该方法的作用是返回当前容器对象中索引值为index的元素的内容。
d、indexof方法
int indexof(object o)
该方法的作用是查找当前容器中是否存在对象o,如果存在则返回该对象第一次出现位置的索引值,如果不存在则返回-1。
另外一个方法lastindexof则是从末尾向前查找,返回从末尾向前第一次出现位置的索引值,如果不存在则返回-1。
e、remove方法
object remove(int index)
该方法的作用是删除索引值为index的对象的内容,如果删除成功则返回被删除对象的内容。
另外一个remove方法:
boolean remove(object o)
该方法的作用是删除对象内容为o的元素,如果相同的对象有多个,则只删除索引值小的对象。如果删除成功则返回true,否则返回false。
无论使用哪一个remove方法,类内部都自动移动将被删除位置后续的所有元素向前移动,保证索引值的连续性。
f、set方法
object set(int index, object element)
该方法的作用是修改索引值为index的内容,将原来的内容修改成对象element的内容。
g、size方法
int size()
该方法的作用是返回当前容器中已经存储的有效元素的个数。
h、toarray方法
object[] toarray()
该方法的作用是将当前容器中的元素按照顺序转换成一个object数组。
下面是一个简单的以arraylist类为基础实现的list系列中类基本使用的示例,代码如下:
import java.util.*;
/**
* 以arraylist类为基础演示list系列类的基本使用
*/
public class arraylistuse {
public static void main(string[] args) {
//容器对象的初始化
list list = new arraylist();
//添加数据
list.add("1");
list.add("2");
list.add("3");
list.add("1");
list.add("1");
//插入数据
list.add(1,"12");
//修改数据
list.set(2, "a");
//删除数据
list.remove("1");
//遍历
int size = list.size(); //获得有效个数
//循环有效索引值
for(int i = 0;i
system.out.println((string)list.get(i));
}
}
}
该程序的运行结果为:
12
a
3
1
1
在list系列中,还包含了stack(栈)类和vector(向量)类,stack类除了实现list系列的功能以外,还实现了栈的结构,主要实现了出栈的pop方法和入栈的push方法。
而vector类由于需要兼容老版本jdk中缘故,所以在实现的方法中需要提供老版本vector类中对应的方法,这样导致vector类中相同或类似的功能方法一般是成对出现的。
set系列
set系列中的类都实现了set接口,该系列中的类均以set作为类名的后缀。该系列中的容器类,不允许存储重复的元素。也就是当容器中已经存储一个相同的元素时,无法实现添加一个完全相同的元素,也无法将已有的元素修改成和其它元素相同。
set系列中类的这些特点,使得在某些特殊场合的使用比较适合。
该系列中常见的类有:
1、copyonwritearrayset
以数组为基础实现的set类。
2、hashset
以哈希表为基础实现的set类。
3、linkedhashset
以链表为基础实现的set类。
4、treeset
以树为基础实现的set类。
以不同的数据结构类型实现的set类,拥有不同数据结构带来的特性,在实际使用时,根据逻辑的需要选择合适的set类进行使用。
set系列中的类的方法和list系列中的类的方法要比list系列中少很多,例如不支持插入和修改,而且对于set系列中元素的遍历也需要转换为专门的iterator(迭代器)对象才可以进行遍历,遍历时顺序和set中存储的顺序会有所不同。
下面是以hashset类为基础实现的示例代码,代码如下:
import java.util.*;
/**
* 以hashset为基础演示set系列类的基本使用
*/
public class hashsetuse {
public static void main(string[] args) {
//容器对象的初始化
set set = new hashset();
//添加元素
set.add("1");
set.add("2");
set.add("3");
set.add("1");
set.add("1");
//删除数据
//set.remove("1");
//遍历
iterator iterator = set.iterator();
while(iterator.hasnext()){
system.out.println((string)iterator.next());
}
}
}
该程序的运行结果为:
3
2
1
map系列
map系列中的类都实现了map接口,该系列中的部分类以map作为类名的后缀。该系列容器类存储元素的方式和以上两种完全不同。
map提供了一种使用“名称:值”这样的名称和数值对存储数据的方法,在该存储方式中,名称不可以重复,而不同的名称中可以存储相同的数值。具体这种存储的格式将在示例代码中进行实现。
在这种存储结构中,任何不为null的对象都可以作为一个名称(key)来作为存储的值(value)的标识,使用这种形式更利于存储比较零散的数据,也方便数据的查找和获得。map类中存储的数据没有索引值,系统会以一定的形式索引存储的名称,从而提高读取数据时的速度。
该系列中常见的类有:
1、hashmap
以hash(哈希表)为基础实现的map类。
2、linkedhashmap
以链表和hash(哈希表)为基础实现的map类。
3、treemap
以树为基础实现的map类。
和上面的结构类似,以不同的数据结构实现的map类,拥有不同数据结构的特点,在实际的项目中使用时,根据需要选择合适的即可。
该系列的类中常见的方法如下:
a、get方法
object get(object key)
该方法的作用是获得当前容器中名称为key的结构对应的值。
b、keyset方法
set keyset()
该方法的作用是返回当前容器中所有的名称,将所有的名称以set的形式返回。使用这个方法可以实现对于map中所有元素的遍历。
c、put方法
object put(object key, object value)
该方法的作用是将值value以名称key的形式存储到容器中。
d、putall方法
void putall(map t)
该方法的作用是将map对象t中的所有数据按照原来的格式存储到当前容器类中,相当于合并两个map容器对象。
e、remove方法
object remove(object key)
该方法的作用是删除容器中名称为key的值。
f、size方法
int size()
该方法的作用是返回当前日期中存储的名称:值数据的组数。
g、values方法
collection values()
该方法的作用是返回当前容器所有的值组成的集合,以collection对象的形式返回。
下面是一个简单的示例,在该示例中演示map系列类的基本使用,代码如下:
import java.util.*;
/**
* 以hashmap为基础演示map系列中类的使用
*/
public class hashmapuse {
public static void main(string[] args) {
//容器对象的初始化
map map = new hashmap();
//存储数据
map.put("苹果", "2.5");
map.put("桔子", "2.5");
map.put("香蕉", "3");
map.put("菠萝", "2");
//删除元素
map.remove("桔子");
//修改元素的值
map.put("菠萝", "5");
//获得元素个数
int size = map.size();
system.out.println("个数是:" + size);
//遍历map
set set = map.keyset();
iterator iterator = set.iterator();
while(iterator.hasnext()){
//获得名称
string name = (string)iterator.next();
//获得数值
string value = (string)map.get(name);
//显示到控制台
system.out.println(name + ":" + value);
}
}
}
该程序的运行结果为:
个数是:3
香蕉:3
菠萝:5
苹果:2.5
使用示例
如前所述,集合框架中的类只是提供了一种数据存储的方式,在实际使用时,可以根据逻辑的需要选择合适的集合类进行使用。
下面以一个字符串计算的示例演示集合类的实际使用。
该程序的功能为计算一个数字字符串,例如”1+2*31-5”、”12*30/34-450”等,的计算结果,在该示例中支持四则运算,但是不支持括号。本示例中计算的字符串要求合法。
该程序实现的原理是:首先按照运算符作为间隔,将字符串差分为数字字符串和运算符字符串的序列,由于分拆出的字符串数量不固定,所以存储到list系列的vector容器中,然后按照运算符的优先级进行计算。
该程序的代码如下:
import java.util.*;
/**
* 计算字符串的值
*/
public class calcstr {
public static void main(string[] args) {
string s = "1+20*3/5";
double d = calc(s);
system.out.println(d);
}
/**
* 计算字符串的值
* @param s 需要计算的字符串
* @return 计算结果
*/
public static double calc(string s){
//拆分字符串
vector v = split(s);
//print(v); //测试代码
//计算字符串
double d = calcvector(v);
return d;
}
/**
* 将字符串拆分为数字和运算符。
* 例如:"1+23*4"则拆分为:"1"、"+"、"23"、"*"和"4"
* @param s 需要拆分的字符串
* @return 拆分以后的结果
*/
private static vector split(string s){
vector v = new vector();
string content = "";
int len = s.length(); //字符串长度
char c;
for(int i = 0;i
c = s.charat(i);
//判断是否为运算符
if(c == '+' ||
c == '-' ||
c == '*' ||
c == '/'){
//存储数字
v.add(content);
//存储运算符
v.add("" + c);
//清除已有字符串
content = "";
}else{
content += c; //连接字符串
}
}
v.add(content); //添加最后一个数字
return v;
}
/**
* 测试代码,输出拆分以后的结果
* @param v 需要打印的vector对象
*/
private static void print(vector v){
int size = v.size();
for(int i = 0;i
system.out.println((string)v.get(i));
}
}
/**
* 计算vector中的数据
* @param v 存储拆分后字符串的vector
* @return 计算结果
*/
private static double calcvector(vector v){
int index1;
int index2;
//计算乘除
while(true){
index1 = v.indexof("*"); //乘号索引值
index2 = v.indexof("/"); //除号索引值
//无乘除符号
if(index1 == - 1 && index2 == -1){
break; //结束循环
}
//如果有乘号
if(index1 != -1){
//没有除号或乘号在前
if(index2 == -1 || index1
string s1 = (string)v.get(index1 - 1); //第一个数字
string opr = (string)v.get(index1); //运算符
string s2 = (string)v.get(index1 + 1); //第二个数字
//计算
string answer = calc(s1,s2,opr);
//计算以后的处理
handle(answer,index1 - 1,v);
}
}
//有除号
if(index2 != -1){
//没有乘号或除号在前
if(index1 == -1 || index2
string s1 = (string)v.get(index2 - 1); //第一个数字
string opr = (string)v.get(index2); //运算符
string s2 = (string)v.get(index2 + 1); //第二个数字
//计算
string answer = calc(s1,s2,opr);
//计算以后的处理
handle(answer,index2 - 1,v);
}
}
}
//计算加
int index3 = v.indexof("+");
while(index3 != -1){ //有加号
string s1 = (string)v.get(index3 - 1); //第一个数字
string opr = (string)v.get(index3); //运算符
string s2 = (string)v.get(index3 + 1); //第二个数字
//计算
string answer = calc(s1,s2,opr);
//计算以后的处理
handle(answer,index3 - 1,v);
//获得下一个加号的位置
index3 = v.indexof("+");
}
//计算减
index3 = v.indexof("-");
while(index3 != -1){ //有加号
string s1 = (string)v.get(index3 - 1); //第一个数字
string opr = (string)v.get(index3); //运算符
string s2 = (string)v.get(index3 + 1); //第二个数字
//计算
string answer = calc(s1,s2,opr);
//计算以后的处理
handle(answer,index3 - 1,v);
//获得下一个减号的位置
index3 = v.indexof("-");
}
//反馈结果
string data = (string)v.get(0);
return double.parsedouble(data);
}
/**
* 计算两个字符串类型的值运算结果
* @param number1 数字1
* @param number2 数字2
* @param opr 运算符
* @return 运算结果
*/
private static string calc(string number1,string number2,string opr){
//将字符串转换为数字
double d1 = double.parsedouble(number1);
double d2 = double.parsedouble(number2);
//判断运算符
if(opr.equals("+")){
return "" + (d1 + d2);
}
if(opr.equals("-")){
return "" + (d1 - d2);
}
if(opr.equals("*")){
return "" + (d1 * d2);
}
if(opr.equals("/")){
return "" + (d1 / d2);
}
return "0"; //运算符错误时返回0
}
/**
* 计算以后的处理
* @param answer 计算结果
* @param index 参与计算的三个字符串中第一个字符串的起始位置
* @param v 存储字符串的容器
*/
private static void handle(string answer,int index,vector v){
//删除计算过的字符串
for(int i = 0;i
v.remove(index);
}
//将计算结果插入到index位置
v.insertelementat(answer, index);
}
}
该程序的运行结果为:
13.0
======================================================
在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定 这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/