1. 什么是容器
在Java当中,如果有一个类专门用来存放其它类的对象,这个类就叫做容器,或者就叫做集合,集合就是将若干性质相同或相近的类对象组合在一起而形成的一个整体。
为什么不用数组呢?
数组对于存放数据来说有它的局限性,比如给一个满数组中再加入新的元素,这时就非常麻烦。
数组的所有功能通过Arraylist容器都可以实现,只是实现的方式不同,如果非要将容器当做一个数组来使用,通过toArray方法返回的就是一个数组。
2.都有哪些容器
说明:
Collection接口定义了存取对象的方法,其子接口Set和List定义了存储方法。Map定义了存储键值对的方法。他们都要import java.util.*
Set中的数据没有顺序,不能重复。
List中的数据有顺序,可以重复。
3.Collection接口
先看一个简单的程序demo1
import java.util.*;
class New{
String name;
New(String name){
this.name = name;
}
public String toString(){
return name;
}
}
public class demo {
public static void main(String[] args) {
Collection c = new ArrayList();
// 这里用到了多态
// c.add的方法的参数是对象类型,而不是普通数据类型
c.add("hellow");
c.add(new Integer(500));
c.add(new New("周佳乐"));
System.out.println(c.size());
System.out.println(c);
}
}
Collection的其他方法去查api
看第二个程序demo2
import java.util.*;
class New{
String name;
New(String name){
this.name = name;
}
public String toString(){
return name;
}
}
public class demo {
public static void main(String[] args) {
Collection c = new HashSet();
// 这里用到了多态
c.add("hellow");
c.add(new Integer(500));
c.add(new New("周佳乐"));
//remove方法在集合中用于删除集合中的对象,在其内部通过equals方法找到并删除对象,返回值为boolean类型的
c.remove("hellow");
// 删除成功,因为"hellow" == "hellow"
c.remove(new Integer(500));
// 删除成功,因为为Integer内部重写了equals方法
System.out.println(c.remove(new New("周佳乐")));
// 删除失败,因为自定义的类没有重写equals方法,这两个的引用不相等,输出false
System.out.println(c);
// 输出[周佳乐]
}
}
我们试试重写自定义类的equals方法
import java.util.*;
class New{
String name;
New(String name){
this.name = name;
}
public String toString(){
return name;
}
// 重写了equals方法
public boolean equals(Object obj){
// 是测试它左边的对象是否是它右边的类的实例
if(obj instanceof New){
New demo = (New) obj;
// 强制类型转换一次
return name.equals(demo.name);
}
// 如果传来的参数不是New的类对象,那么直接交给Object处理
return super.equals(obj);
}
// 重写hashCode方法
// 因为存储是按键值对的形式存储的(HashSet),值和键(索引)的方法都要重写
public int hashCode(){
// 字符串内部已经实现了hashCode方法
return name.hashCode();
}
}
public class demo {
public static void main(String[] args) {
Collection c = new HashSet();
// 这里用到了多态
c.add(new New("周佳乐"));
System.out.println(c.remove(new New("周佳乐")));
System.out.println(c);
// 输出true []
}
}
4.Iterateor接口
Interateor:迭代器
所有实现了Collection接口的容器类都有一个interateor方法,返回一个Interateor接口的对象。该对象用以方便的实现容器内部元素的遍历操作。
Iterateor接口定义了如下的方法:
1.boolean hasNext();
2.Object next();
3.void remove();
看下面的代码
// 在main函数里
Collection c = new HashSet();
c.add(new New("zjl"));
c.add(new New("abcde"));
c.add(new New("zjd"));
// 调用iterator方法,将返回值赋给一个Iterator类型的引用
Iterator i = c.iterator(); // 必须执行
// 当容器内部下一个位置还有值时
while(i.hasNext()){
// 拿到这个对象(多态)
New newone = (New)i.next();
System.out.println(newone);
}//实现了无顺序的遍历
注:若想要用remove方法,必须用Iterator的引用的remove方法,而不能用Collection的,因为Ilterator内部有锁机制。
5.Set接口
Set是Collection接口的子接口,并没有加入新的方法,只不过要求存入的数据是不能重复的。
Set的容器类主要有HashSet,TreeSet
看一个简单的代码
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
Set a1 = new HashSet();
a1.add("123");
a1.add("456");
a1.add("123"); // 再加入equals值相等的对象是不成功的,虽然程序不报错,但是输出的结果中只有一个123
System.out.println(a1);
}
}
接下来看Set的其他方法
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
Set a1 = new HashSet();
Set a2 = new HashSet();
a1.add("a");
a1.add("b");
a1.add("c");
a2.add("b");
a2.add("c");
a2.add("d");
Set an = new HashSet(a1); // 新建一个Hashset对象an,并把a1的值赋给an
an.retainAll(a2); // retainAll方法用于求两个容器中的交集,返回值是bollean类型的
System.out.println(an); // 输出结果是 b c
a2.addAll(a1); // addAll方法用于求两个容器的和,当然相同的值不能再加进去
System.out.println(a2); // 输出结果是a b c d
}
}
6.liST接口
List也是Collection接口的子接口,并没有加入新的方法,只不过存入的数据是有序的。
List的容器类主要有ArrayList,LinkedList。
除此之外,再List的内部还有一个索引,用于按序号操作List中的数据。
下面是List的一些常用方法
此外,java.util.Collections 提供了一些常用的静态方法如下
看下面一段程序
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
List l1 = new ArrayList();
for(int i=0 ; i<5 ; i++){
l1.add("l"+i);
}
System.out.println(l1); // l0, l1, l2, l3, l4
// 由于Collections中的方法大多都是静态的,所以直接用类名调用
Collections.reverse(l1); // 倒置
System.out.println(l1); // l4, l3, l2, l1, l0
Collections.shuffle(l1); // 打乱
System.out.println(l1); // l0, l4, l1, l3, l2
System.out.println(Collections.binarySearch(l1,"l4")); // 利用二分法查找
}
}
那么问题来了,如果List容器里是一些抽象的变量,那调用Collections.sort内部是如何实现排序的。
实际上,所有可排序的类都实现了java.lang.Compareable接口,而Compareable接口中只有一个方法
public int compareTo(object obj);返回值为0,相等,为1,this大,为-1,obj大。
我们的对象可以通过实现Compareable接口中的compareTo方法,确定该类对象的排序方式,进而再调用Collections.sot方法时,实际上是通过类中的compareTo方法先进行比较,再排序。
所以我们要先实现并重写对应的比较大小的方法。
package jihe;
public class Person implements Comparable {
String name;
public Person(String name) {
this.name = name;
}
public String toString() {
return name;
}
public boolean equals(Object obj) {
// 是测试它左边的对象是否是它右边的类的实例
if (obj instanceof Person) {
Person demo = (Person) obj;
// 强制类型转换一次
return name.equals(demo.name);
}
return super.equals(obj);
}
public int hashCode() {
return name.hashCode();
}
@Override
// 在这里我们自定义了自己的比较大小的算法
public int compareTo(Object o) {
Person p = (Person) o;
// 字符串的比较大小可以直接拿来用
return name.compareTo(p.name);
}
}
再看看排序
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
List l1 = new ArrayList();
l1.add(new Person("abc"));
l1.add(new Person("acb"));
l1.add(new Person("cba"));
l1.add(new Person("周佳乐"));
l1.add(new Person("周杰伦"));
// Collections内部没有比较大小,实际上它使用了参数对象里的比较大小的方式,也就是我们实现的compareTo方法
Collections.sort(l1);
System.out.println(l1);
// [abc, acb, cba, 周佳乐, 周杰伦]
}
}
关于Linked和Array的选择
- 如果需要查的快:Array
- 如果需要改的块:Linked
- 而Hash介于他们之间
7.Map
实现Map接口的类用于存放键值对的信息。
Map的实现类有HashMap和TreeMap(红黑树)等。
Map类中存储的信息中,键不能重复。
常用方法
对应的demo
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
Map m1 = new HashMap();
Map m2 = new HashMap();
m1.put("one",new Integer(1));
//jdk1.5之后也可以这么写: m2.put("A",1);
m1.put("two",new Integer(2));
m1.put("three",new Integer(3));
m2.put("A",new Integer(1));
m2.put("B",new Integer(2));
System.out.println(m1.containsKey("one")); // 返回值是boolean,输出ture
System.out.println(m2.containsValue(new Integer(1))); // 输出ture
int i = (Integer) m2.get("A"); // m2.get("A")得到对应的值(object)
System.out.println(i);
}
}
jdk1.5提供了自动打包和自动解包
打包:基本类型转换成对象
解包:对象转换为基本类型
关于统计一个数组中,有多少个相等的值的算法
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
final Integer one = new Integer(1); // 设一个不能改变的值1
String Strarr [] = {"aaa","bbb","aaa","aaa","ccc"}; // 设一个字符串的数组
Map m1 = new HashMap();
for (int i=1 ; i<Strarr.length ; i++){
Integer num = (Integer) m1.get(Strarr[i]); // 得到键为Strarr[i]的值,强转成Integer,赋给num
// 一开始num的值是空的,得到一样的键值时,num就有值了
m1.put(Strarr[i] , num == null? 1 : new Integer(num+1)); //
// 保存键值对
}
System.out.println(m1);
}
}
用自动打包机制
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
final int one = 1; // 设一个不能改变的值1
String Strarr [] = {"aaa","bbb","aaa","aaa","ccc"}; // 设一个字符串的数组
Map m1 = new HashMap();
for (int i=1 ; i<Strarr.length ; i++){
int num = (Integer) m1.get(Strarr[i]) == null ? 0 : (Integer) m1.get(Strarr[i]); //
// 一开始num的值是空的,得到一样的键值时,num就有值了
m1.put(Strarr[i] , num == 0 ? one : num+1); //
// 保存键值对
}
System.out.println(m1);
}
}
8.泛型
起因:JDK1.4前不明确,装入集合中的类型都被当做Object类型使用,而从集合中取出时有需要进行强制转换,比如:
// 在讲Colltions类时,我们讨论如何对抽象的类进行排序,我们可以重写compareTo方法
public int compareTo(Object o) {
Person p = (Person) o;
// 字符串的比较大小可以直接拿来用
return name.compareTo(p.name);
// 在讲到Iterateor接口时,我们遍历Set集合中的数据
while(i.hasNext()){
// 拿到这个对象(多态)
New newone = (New)i.next();
System.out.println(newone);
}//实现了无顺序的遍历
我们想在定义集合时,同时能定义了集合中的数据的类型,就要引入泛型,引入泛型可以增强代码的可读性和稳定性
使用:在可以定义泛型的类/接口时,在后面加上<类型(比如说是String)>,就可以
看代码
import java.util.*;
import jihe.*;
public class demo {
public static void main(String[] args) {
List <String> l1 = new ArrayList<String>();
l1.add("123");
l1.add("456");
for(int i=0 ; i<l1.size() ; i++){
String s = l1.get(i);
// 没用到泛型时,我们还要对返回的对象进行强制类型转换
System.out.println(s);
}
}
}
Compareable接口也可以用到泛型,每一次需要强制类型转换的时候大多数都能用泛型解决,但是对于能加泛型的类或接口,api有提示。
9.总结(116)
1个图
1个类:Collections
6个接口:Collection,Set,List,Map,Iterateot,Compareable
几个知识点:
1.obj instanceof ClassName,用于判断一个对象是否为一个类(或接口、抽象类、父类)的实例.