Java
集合框架
重写 hashCode() 和 equals() 方法
Object 类
Java.lang.Objiect 类是一个超级类,是所有类的父类,在不显示声明类扩展自某个类的情况下,Java 会自动把Object 作为要定义类的父类,可以使用类型为Object 的引用变量指向任意类型的对象。
protected Object clone() 创建与该对象的类相同的类
public boolean equals(Object obj) 比较两个对象是否相等
public int hashCode() 返回该对象的散列码int值
public Strint toString() 返回该对象的字符串表示
public final void notify() 激活等待对象的一个线程
public final void notifyAll() 激活等待在该对象上的全部线程
public final void wait() 等待这个对象另一个更改线程的通知
protected void finalize() 当垃圾收集器确定不存在对该对象的更多引用时,垃圾收集器调用该方法
当程序调用 System.out.println() 方法时 Object类的 toString() 方法被调用
public class ToString {
public static void main(String args[]) {
//创建ToString的对象
ToString ts=new ToString();
System.out.println(ts);
}
}
编译运行后得到:
F:\gz\JAVA\jhkj>java ToString
ToString@15db9742
重写equals() 方法
使用“==”运算符可以比较两个引用是否引用相同的对象,而equals() 方法用于比较两对象是否相等
public class EqualsTest {
public static void main(String args[]) {
Double d1=new Double("99.9");
Double d2=new Double("99.9");
if(d1==d2) {
System.out.println("d1!=d2");
}
if(d1.equals(d2)) {
System.out.println("d1=d2");
}
}
}
运行后得到:
F:\gz\JAVA\jhkj>java EqualsTest
d1=d2
需要比较两个对象在意义上是否相等时,需要重写Object类的 equals() 方法,否则只有当两个引用引用相同的对象时,这两个对象才被认为是相等的
①对于任何引用类型,o.equals(o)==true 成立
②如果o.equals(o1)==true 成立,那么o1.equals(o)==true 也一定成立
③如果o.equals(o1)==true成立且o.equals(o2)==true成立,那么o1.equals(o2)==true也成立
④如果第一次调用o.equals(o1)==true成立,则在o和o1没有改变的情况下,以后的任何次调用都成立
⑤equals(null)==true任何时间都不成立
重写hashCode() 方法
散列码是通过一种不可逆的散列(Hash)算法,对一个数据进行计算,获得的一个“唯一”的值。这个值可以对这个数据进行标识,在查找数据时,可以通过这个值来快速定位数据,而有效的减少系统的开销
由于散列码的长度是有限的和固定不变的,因此在数据极多的情况下,散列值会出现重复,即“碰撞”。
重写约定:
①在Java 程序执行期间,同一个对象调用hashCode()方法必须返回同一个值(同一个应用执行期)
②如果两个对象进行 equals() 比较是相等的,那么在这两个对象的任何一个上调用hashCode()方法必须都得到相等的结果
③如果两个对象进行equals() 比较不相等,那么它们的调用 hashCode() 方法不必返回完全不同的结果,对不同的对象产生不同的(散列码)hashCode,有可能提升哈希表(hashtable)的效率
重写了 equals() 方法就必须重写 hashCode() 方法,根据前面介绍的重写约定,下面代码给出了hashCode() 方法和 equals() 方法的参考实现,此代码既重写了 equals() 方法,也重写了hashCode() 方法:
public class EqualsHashCode {
//主方法
public static void main(String args[]) {
//创建Test类的对象t1和t2
Test t1=new Test("test",20);
Test t2=new Test("test",20);
//打印t1和t2对象的equals()比较结果
System.out.println(t1.equals(t2));
System.out.println(t2.equals(t1));
}
}
class Test {
//声明成员变量
String name;
int age;
//Test类的有参构造器
public Test(String name,int age) {
this.name=name;
this.age=age;
}
//重写Object类的equals()方法
public boolean equals(Object obj) {
if(!(obj instanceof Test)) {
return false;
}
//强制类型转换引用类型
Test temp=(Test)obj;
//比较成员属性
if(this.name.equals(temp.name)
&&this.age==temp.age) {
return true;
}
return false;
}
//重写Object类的hashCode()方法
public int hashCode() {
return 17*age;
}
}
可以根据需要编写自己的 hashCode() 和 equals()方法,但是一定要遵守重写的约定,编译运行以上代码:
F:\gz\JAVA\jhkj>java EqualsHashCode
true
true
Java中,对对象的操作是基于引用的,当需要对一组对象进行操作时,就需要有接收这一组引用的容器,平时最常用的就是数组。
集合框架——操作一组对象引用的容器
使得应用程序可以很容易的通过改变一个实现来进行调整
集合框架中常用的接口及其实现类
Collection Set接口,List接口
Set HashSet类, LinkedHashSet类 , SortedSet接口
List LinkedList类 , Vector类, ArrayList类
SortedSet TreeSet类
Map Hashtable类, LinkedHashMap类, HashMap类, SortedMap接口
SortedMap TreeMap类
使用集合框架主要可以进行以下几种操作
①将对象添加到集合中
②从集合中查找某个对象或者一组对象
③从集合中删除对象
④遍历集合,及查看集合中的每一个对象
Set接口及其实现类
该接口继承自 Collection借口,不允许将两个相同的对象放入其中,主要实现类有:HashSet类, LinkedHashSet类,SortedSet接口实现类——TreeSet类
boolean add(E e) 将对象添加到集合中,参数e 是要添加的对象
int size() 返回集合中元素的个数
boolean isEmpty() 判断集合是否为空,为空则返回false,否则返回true
boolean contains(Object o) 判断集合中是否存在和o相等的元素,有就返回true,否则返回false
Iterator<E>iterator() 返回Iterator类的对象,主要在遍历集合时使用
boolean remove(Object o) 从集合中删除元素,参数o 为要删除的元素
void clear() 删除集合中的所有元素,清空集合
HashSet类
是Set接口的主要实现类之一,HashSet中的元素没有被排序和分类,如果在遍历集合时不关心集合中元素的顺序,可以使用HashSet类
import java.util.HashSet;
public class HashSetTest {
//主方法
public static void main(String args[]) {
//创建HashSet类的对象
HashSet hs= new HashSet();
//将元素添加到集合中
hs.add("aa");
hs.add("bb");
hs.add("cc");
//声明变量size用于存放集合的
//size()方法返回的集合元素的个数
int size=hs.size();
System.out.println("集合中元素的个数为: "+size);
//从集合中删除元素
if(hs.remove("aa")) {
//如果删除成功则打印
System.out.println("删除成功!");
System.out.println("集合中剩余的元素个数为: "
+hs.size());
}
//删除集合中的所有元素
hs.clear();
//如果删除成功则打印
System.out.println("集合中剩余的元素个数为: "
+hs.size());
//判断集合是否为空
if(hs.isEmpty()) {
//如果为空则打印
System.out.println("集合中没有任何元素!");
}
}
}
创建HashSet类的对象和将元素添加到集合中以及HashSet类中部分方法的使用
编译运行后得到结果:
F:\gz\JAVA\jhkj>java HashSetTest
集合中元素的个数为: 3
删除成功!
集合中剩余的元素个数为: 2
集合中剩余的元素个数为: 0
集合中没有任何元素!
LinkedHashSet类
同为Set接口的主要实现类之一,但是与HashSet类不同的是,LinkedHashSet类是按照元素的插入顺序排序的。所以如果想保持元素的插入顺序,则使用LinkedHashSet类
import java.util.*;
public class LinkedHashSetTest {
//主方法
public static void main(String args[]) {
//创建HashSet类的对象
LinkedHashSet lhs = new LinkedHashSet();
//将元素添加到集合中
lhs.add("aa");
lhs.add("bb");
lhs.add("cc");
//获取集合的迭代器
Iterator it=lhs.iterator();
//对集合进行遍历
while(it.hasNext()) {
//打印集合中的元素
System.out.println(it.next());
}
//声明变量size用于存放集合的
//size()方法返回的集合元素的个数
int size=lhs.size();
System.out.println("集合中元素的个数为: "+size);
//从集合中删除元素
if(lhs.remove("aa")) {
//如果删除成功则打印
System.out.println("删除成功!");
System.out.println("集合中剩余的元素个数为: "
+lhs.size());
}
//删除集合中的所有元素
lhs.clear();
//如果删除成功则打印
System.out.println("集合中剩余的元素个数为: "
+lhs.size());
//判断集合是否为空
if(lhs.isEmpty()) {
//如果为空则打印
System.out.println("集合中没有任何元素!");
}
}
}
编译运行后得到结果:
F:\gz\JAVA\jhkj>java LinkedHashSetTest
aa
bb
cc
集合中元素的个数为: 3
删除成功!
集合中剩余的元素个数为: 2
集合中剩余的元素个数为: 0
集合中没有任何元素!
LinkedHashSet 是有序的,从输出结果可以看出,按照元素插入的顺序排序
TreeSet类
该类是SortedSet 接口的实现类,TreeSet是Java 集合中两种分类集合之一,另一个是TreeMap类,
TreeSet按照元素的自然顺序进行升序排列
TreeSet类提供了四个构造函数:TreeSet() TreeSet(Collection c), TreeSet(Comparator comparator) 和 TreeSet(SortedSet s)
所谓自然顺序,就是元素天生就具有的顺序,是不能认为改变的顺序
import java.util.*;
public class TreeSetTest {
//主方法
public static void main(String args[]) {
//创建TreeSet类的对象
TreeSet ts = new TreeSet();
//将元素添加到集合中
ts.add("sh");
ts.add("cq");
ts.add("bj");
ts.add("cd");
//获取集合的迭代器
Iterator it=ts.iterator();
//对集合进行遍历
while(it.hasNext()) {
//打印集合中的元素
System.out.println(it.next());
}
}
}
首先创建了TreeSet类的对象,然后向其中插入一些元素,再通过迭代器对集合进行遍历
编译运行得到结果:
F:\gz\JAVA\jhkj>java TreeSetTest
bj
cd
cq
sh
从输出结果可以看出,元素是按照字母顺序输出的
List接口及其实现类
List接口继承自 Collection接口,List 允许将两个相同的对象放入其中,并且可以根据索引插入和访问列表中的元素
List接口的主要实现类有:LinkedList 类, Vector 类, ArrayList 类
boolean add(E e) 将对象添加到列表中,参数e是要添加的对象
Object get(int index) 返回List中指定位置的元素
int indexOf(Object o) 返回第一个出现元素o的位置,否则返回-1
int lastIndexOf(Object o) 返回最后一个出现元素o的位置,否则返回-1
Object set(int index,Object element) 用元素element 取代位置index上的元素,并且返回旧的元素
int size() 返回列表中元素的个数
boolean isEmpty() 判断列表是否为空,如果为空则返回false,否则返回true
boolean contains(Object o) 判断列表中是否存在和o 相等的元素,如果有则返回true,否则返回false
ListIterator listIterator() 返回一个列表迭代器,用来访问列表中的元素
Object remove(int index) 删除指定位置上的元素
void clear() 删除列表中的所有元素
LinkedList 类
该类是用双链表实现的,列表中的元素按照索引位置排序,当需要进行快速插入和删除操作时,使用LinkedList
下面的例子首先创建了LinkedList类的对象,然后向其中插入了一些元素,再通过迭代器对集合进行遍历
import java.util.*;
public class LinkedListTest {
//主方法
public static void main(String args[]) {
//创建LinkedList类的对象
LinkedList ll=new LinkedList();
//将元素添加到列表中
ll.add("shanghai");
ll.add("chongqing");
ll.add("beijing");
ll.add("chengdu");
//获取列表的迭代器
Iterator it=ll.iterator();
//对列表进行遍历
while(it.hasNext()) {
//打印列表中的元素
System.out.println(it.next());
}
//调用set方法
ll.set(2,"tianjin");
//按索引访问列表中的元素
System.out.println("按索引访问:"+ll.get(3));
}
}
编译运行后得到:
F:\gz\JAVA\jhkj>java LinkedListTest
shanghai
chongqing
beijing
chengdu
按索引访问:chengdu
列表索引是从0开始计算的,上面代码中ll.get(3)访问列表中索引为3的元素,即列表中的第四个元素,为chengdu
ArrayList 类
是用数组实现的,可以把它看作是一个可以变长的数组,ArrayList中的元素也是按照索引位置排序的,该类提供了三个构造器: ArrayList() ,ArrayList(Collection c),ArrayList(int initialCapacity)
import java.util.*;
public class ArrayListTest {
//主方法
public static void main(String args[]) {
//创建LinkedList类的对象
ArrayList al=new ArrayList();
//将元素添加到列表中
al.add("shanghai");
al.add("chongqing");
al.add("beijing");
al.add("chengdu");
//获取列表的迭代器
int size=al.size();
//对列表进行遍历
for(int i=0;i<size;i++) {
//打印列表中的元素
//按索引访问列表中的元素
System.out.println(al.get(i));
}
}
}
首先创建ArrayList类的对象,然后向其中插入一些元素,再通过for循环对集合进行遍历
编译运行得到如下结果:
F:\gz\JAVA\jhkj>java ArrayListTest
shanghai
chongqing
beijing
chengdu
对集合进行遍历时,也可以先取得集合或者列表中元素的个数,再使用for循环进行遍历
Vector类
基本与 ArrayList类相同,但不同的是,Vector中的方法被同步,是线程安全的
在使用 枚举 对Vector进行遍历时,从集合中取出的元素都是Object类型的,需要强制将类型转换成为元素本身的类型,其他集合也一样
Map接口及其实现类
与List Set 接口不同的是,Map 不是继承自Collection接口。Map接口用于维护键/值(key / value pairs),该接口描述了不能重复的键到值的映射,键和值都是对象
主要的实现类有: Hashtable 类 LinkedHashMap 类 HashMap 类 SortedMap子接口的实现类——TreeMap类
Hashtable 类
基本与HashMap一样,但是Hashtable被同步了,且Hashtable中不允许有null的键和null的值,而HashMap允许
JavaAPI 为 Hashtable类提供的四个构造器:Hashtable() Hashtable(int initialCapacity) Hashtable(int initialCapacity,float loadFactor) Hashtable(Map map)
下面的例子,首先创建了 Hashtable 类的对象,然后向其中插入一些元素,再通过迭代器对集合进行遍历:
import java.util.*;
public class HashtableTest {
//主方法
public static void main(String args[]) {
//创建Hashtable类的对象
Hashtable ht = new Hashtable();
//将元素添加到Hashtable中
//键和值都是String类的对象
ht.put("a","shanghai");
ht.put("b","chongqing");
ht.put("c","beijing");
ht.put("d","chengdu");
//遍历Hashtable--首先取得键的集合
Set ks=ht.keySet();
//取得迭代器
Iterator ii=ks.iterator();
//遍历Hashtable
while(ii.hasNext()) {
//取出键
String key=(String)ii.next();
System.out.println("key="+key+" ");
//通过键取出值
String value=(String)ht.get(key);
System.out.println("value="+value);
}
}
}
编译运行后得到结果:
F:\gz\JAVA\jhkj>java HashtableTest
key=b
value=chongqing
key=a
value=shanghai
key=d
value=chengdu
key=c
value=beijing
遍历Hashtable时,首先应该取得键的集合Set ks=ht.keySet(),再对键的集合进行遍历,取出键所对应的值。在对后面将要介绍的HashMap等进行遍历的时候也可以采取同样的方法。
LinkedHashMap 类
和LinkedHashSet类一样,LinkedHashMap按照元素的插入顺序排序,当需要进行快速遍历操作时,可使用该类
五个构造器:
①LinkedHashMap()
②LinkedHashMap(int initialCapacity)
③LinkedHashMap(int initialCapacity,float loadFactor)
④LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder)
⑤LinkedHashMap(Map map)
首先创建LinkedHashMap类的对象,然后向其中插入一些元素,通过迭代器对集合进行遍历:
import java.util.*;
public class LinkedHashMapTest {
public static void main(String args[]) {
//创建LinkedHashMap类的对象
LinkedHashMap lhm = new LinkedHashMap();
//键和值都是String类的对象
lhm.put("b","B");
lhm.put("c","C");
lhm.put("a","A");
lhm.put("d","D");
//遍历LinkedHashMap--首先取得键的集合
Set ks=lhm.keySet();
//取得迭代器
Iterator ii=ks.iterator();
//遍历LinkedHashMap
while(ii.hasNext()) {
//取出键
String key=(String)ii.next();
System.out.print("key="+key+" ");
//通过键取出值
String value=(String)lhm.get(key);
System.out.println("value="+value);
}
}
}
编译运行后得到:
F:\gz\JAVA\jhkj>java LinkedHashMapTest
key=b value=B
key=c value=C
key=a value=A
key=d value=D
可以看出,打印元素的顺序和插入时间相同
HashMap 类
与HashSet类一样,都是未分类,未排序的,
构造函数:
①HashMap()
②HashMap(int initialCapacity)
③HashMap(int initialCapacity,float loadFactor)
④HashMap(Map map)
import java.util.*;
public class HashMapTest {
public static void main(String args[]) {
//创建HashMap类的对象
HashMap hm = new HashMap();
//键和值都是String类的对象
hm.put("b","B");
hm.put("c","C");
hm.put("a","A");
hm.put("d","D");
//对HashMap进行操作
if(hm.containsKey("a")) {
System.out.println("HashMap中已经存在键: a");
}
//从HashMap中取出键为b的值
String valueB=(String)hm.get("b");
System.out.println("b===="+valueB);
//遍历HashMap--首先取得键的集合
Set ks=hm.keySet();
//取得迭代器
Iterator ii=ks.iterator();
//遍历LinkedHashMap
while(ii.hasNext()) {
//取出键
String key=(String)ii.next();
System.out.print("key="+key+" ");
//通过键取出值
String value=(String)hm.get(key);
System.out.println("value="+value);
}
//删除HashMap中的所有元素
hm.clear();
//打印HashMap中元素的个数
System.out.println("元素个数为: "+hm.size());
}
}
编译运行结果为:
F:\gz\JAVA\jhkj>java HashMapTest
HashMap中已经存在键: a
b====B
key=a value=A
key=b value=B
key=c value=C
key=d value=D
元素个数为: 0