2020/4/22 -- Java集合框架和泛型机制
知识要点:
- Java集合框架
- 容器类API
- Set接口
- Comparable/comparable 接口
- List接口
- Map接口
- 容器的泛型操作
- equals和hashcode方法的理解
Java集合框架概述
JDK API中专门设计了一组类,这组类的功能就是实现各种方式的数据存储,用于专门存储其它对象的类,称为对象容器类,这组类和接口的设计结构统称为 集合框架(collection Framework)。
collection接口
collection接口表示一组对象,这些对象也叫做collection接口的元素,一些collection接口是有序的或者不允许有重复的元素。JDK不提供此接口的任何直接实现,而是提供更具体的子接口如set和List实现。
Set存放的元素是无序且不包含重复元素;List接口存放的元素是有序的且允许有重复的元素。
“元素”即对象引用,容器中的元素类型都为Object类型,从容器中取得元素时,必须把它转换成为原来的类型。“重复”是指两个对象通过equals判断相等;“有序”是指元素存入的顺序与取出的顺序相同。
-
容器类中单个元素的添加、删除的方法。
boolean add(Object o);
_将对象添加给集合boolean remove(Object o)
如果集合中有与o匹配的对象,则删除 -
容器中元素查询的方法
int size()
返回当前集合中元素的数量boolean isEmpty()
查找此Collection中是否包含元素boolean contains(Object obj)
查找此collection中是否包含指定的元素boolean contains(Collection c)
判断此Collection是否包含指定Collection中的所有元素Iterator iterator()
返回一个该集合上的迭代器,用来访问该集合中的各个元素。boolean containsAll(Collection c)
查找该集合中是否包含有集合c中的所有元素 -
组操作,作用于元素组或整个集合。
boolean addAll(Collection c)
将指定的集合c中所有元素添加到该集合void clear()
删除集合中所有元素void removeAll(Colletion c)
从集合中删除集合c中的所有元素。retainAll(Collection c)
从当前集合中删除指定集合c中不包含的元素 -
转换操作,集合与数组之间的转换
Object[] toArray()
把Collection转换为对象数组Object[] toArray(Object[] a)
返回一个内含集合所有元素的array。运行期间返回的array和参数a的类型相同,需要转换为正确类型
可以把集合转换后才能为任何其它数组。但是不能把集合转换成为基本数据类型的数组
遍历Collection中的元素一般采用Iterator遍历器。以Collection借口定义的子类集合上都有一个与容器类相对应的遍历器。可以通过遍历器遍历出容器中的各个对象元素。
Iterator借口中定义的方法:
boolean hasNext()
判断游标右边是否有元素
Object next()
返回游标右边的元素并将右边移动到下一个位置
void remove()
删除游标左面的元素
Set接口实现类
Set有七个实现类,常用的实现类有三个,HashSet,TreeSet,LinkedHashSet
根据每哥对象的哈希码值(hashCode())方法获得用固定的算法获得存储索引,把对象存在散列表的想要位置(表元)中:如果计算所得的索引的位置没有元素则直接葌,如果有元素则需要查看是否已经存在该对象,若存在则直接使用该对象,不存在则存入。
Set接口存取删对象都有很高的效率。对与存放到Set容器中的对象,对应的类一定要重写equals和hashCode(Object obj)方法以实现对象相等原则,如: x.equals(y)为true,那么x.hashCode()的值和y.hashCode()的值相同。
实现类HashSet
不保存元素的加入顺序,HashSet类根据元素的哈希码进行存放,所以取出时也可以通过哈希码快速找到。
public static void main(String[] args) {
HashSet hs = new HashSet();
hs.add("zxx");
hs.add("zahx");
Iterator it = hs.iterator();
while(it.hashNext()) {
System.out.println(it.next());
}
}
public class Student {
private int age;
private String name;
Student(int num, String name) {
this.name = name;
this.num = num;
}
//显示Student类的信息,必须重写toString()方法
public String toString() {
return "age: " + age + " name: "+name;
}
// 在java规范中要求,如果用户重写了equals()方法,那么就一定要重写hashCode()方法
// 两个对象进行equals比较时,如果返回true,呢么它们的hashCode要求返回相等的值
public int hashCode() {
return age*name.hashCode();//确定为统一num和相同的name
}
public boolean equals(Object o) {
Student s = (Student) o; // 创建一个新的局部变量
return age == s.age && name.equals(s.name);
}
}
实现类LinkedHashSet
根据哈希码进行存放,同时用链表记录元素的加入顺序。
Set<Student> linkHashSet = new LinkedHashSet<Student>();
linkHashSet.add(new Student("11","aaa"));
linkHashSet.add(null);
Iterator it = linkHashSet.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
通过链表来存储对象,一般插入和删除效率较高,检索效率相等价较低。
实现类TreeSet
使用红黑树结构对加入的元素进行排序存放。
由于TreeSet是自排序的所以在存放自己定义的类的对象时,需要写自己的比较函数实现Comparable接口,通过重写comparaTo()方法实现自定义类的排序。
public class Student {
private int age;
private String name;
Student(int num, String name) {
this.name = name;
this.num = num;
}
//显示Student类的信息,必须重写toString()方法
public String toString() {
return "age: " + age + " name: "+name;
}
// 在java规范中要求,如果用户重写了equals()方法,那么就一定要重写hashCode()方法
// 两个对象进行equals比较时,如果返回true,呢么它们的hashCode要求返回相等的值
public int hashCode() {
return age*name.hashCode();//确定为统一num和相同的name
}
public boolean equals(Object o) {
Student s = (Student) o; // 创建一个新的局部变量
return age == s.age && name.equals(s.name);
}
public int compareTo(Object o) {
Student s = (Student)o;
if (s.getAge().compareTo(this.getAge() ) > 0)
return -1;
if (s.getAge().compareTo(this.getAge() ) == 0)
return 0;
return 1;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
推荐使自然排序与equals一致。
TreeSet自定义比较器,TreeSet有一个构造方法,其允许给定比较器,并根据给定的比较器对元素进行排序。
public class Student {
private int age;
private Integer score;//新增一个成员score
private String name;
Student(int age, String name, int score) {
this.name = name;
this.age = age;
this.socre = score;
}
//显示Student类的信息,必须重写toString()方法
public String toString() {
return "age: " + age + " name: "+name;
}
// 在java规范中要求,如果用户重写了equals()方法,那么就一定要重写hashCode()方法
// 两个对象进行equals比较时,如果返回true,呢么它们的hashCode要求返回相等的值
public int hashCode() {
return age*name.hashCode();//确定为统一num和相同的name
}
public boolean equals(Object o) {
Student s = (Student) o; // 创建一个新的局部变量
return age == s.age && name.equals(s.name);
}
public int compareTo(Object o) {
Student s = (Student)o;
if (s.getAge().compareTo(this.getAge() ) > 0)
return -1;
if (s.getAge().compareTo(this.getAge() ) == 0)
return 0;
return 1;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
//学生年龄比较器
class StudentAgeComparator implements Comparator<Student> {
public int compare(Student o1, Student o2) {
int i = o2.getAge() - o1.getAge();
return i;
}
}
//学生成绩比较器
class StudentScoreComparator implements Comparator<Student> {
public int compare(Student o1, Student o2) {
int i = (int) (o2.getScore() - o1.getScore());
return i;
}
}
public static void main(String[] args) {
//创建TreeSet对象时选择比较器
//选择年龄比较器
Set ts = new TreeSet(new StudentAgeComparator());
}
List 接口实现类
常用的List接口:ArrayList、LinkedList和Vector。
实现类ArrayList
ArrayList类支持可随需要正常的动态数组。保存添加顺序
使用索引取出元素有较高的效率,它可以使用索引来快速定位对象。但元素做删除或插入速度较慢,因为使用了数组,需要移动后面的元素以调整索引顺序。
Collection c1 = new ArrayList(); //实例化一个具体的子类对象
c1.add(new Integer(2));//添加
c1.add("hehe");//添加
System.out.println("c1:" + c1); //打印结果为:[2, hehe];
Collection c2 = new ArrayList();
c2.addAll(c1);//将c1的元素添加到c2中
c2.remove(new Integer(2));//删除
Iterator it = c2.iterator();
while (it.hasNext()) {
Object obj = it.next();
System.out.println("元素:" + obj);
}
//增强for循环来遍历集合中的每一个元素
for (Iterator it = coll.iterator(); it.hasNext();) {
Object obj = it.next();
String str = (String) obj;
System.out.println(str);
}
实现类LinkedList
链接列表数据结构,LinkedList是双向链表实现的容器,所以针对频繁的插入或删除元素使用LinkedList效率较高,适合用于实现Stack和Queue
- addFirst()在列表头部增加元素
- addLast()在列表尾部增加元素
- getFirst()获得第一个元素
- getLash()获得最后一个元素
- removeFrist()删除第一个元素
- removeLast()删除最后一个元素
public static void main(String[] args) {
LinkedList linklist = new LinkedList();
linklist.add("F");
linklist.add("B");
linklist.add("D");
linklist.add("E");
linklist.add("C");
linklist.addLast("Z");
linklist.addFirst("A0");
linklist.add(1,"A1");//在下标为1处添加一个元素
linklist.remove("F");
linklist.remove(2);//删除第二个元素
Object val = linklist.get(2);//获得下标为2的元素
linklist.set(2, (String)var + "changed");
}
实现类Vector
大多数操作与ArrayList类相同,区别之处在于Vector是线程同步的。
addElement(Object obj)
添加到尾部insertElement(Object obj, int index)
把组件加到所定索引处,此后的内容向后移动setElementAt(Object obj, int index)
覆盖原先index内容removeElement(Object obj)
把向量中含有本组件的内容移走removeAllElements()
清空向量
java集合与数组的区别:在创建数组时需要明确指明数组的长度,数组一旦创建不能被改变。java集合中不能存放基本数据类型,只能存放对象的引用。
Map接口
Map接口用于维护键值对的形式(key/value pairs),描述可不重复的键到值的映射。
Map底层的键是用Set来存放的,键对应的类必须重写hashCode和equals方法
Map接口中定义的一些常用方法:
-
添加删除操作
Object put(Object key, Object value)
;放入到映像中,该关键字存在则覆盖原有值,返回关键字旧值,如果原先关键字不存在则返回nullObject remove(Object key)
根据指定的键将键值对从Map中删除void putAll(Map t)
将来自特定映像的所有元素添加给该映像void clear()
清空Map,键值都可以是null,但是不能把mao作为一个键添或值添加给自己
-
元素查询
Object get(Object key)
获得与key关联的的值,返回对象,没有返回nullboolean containKey(Object key)
判断映像中是否存在keyboolean containValue(Object value)
判断映像中是否存在valueint siez()
返回当前映像的映射数量boolean isEmpty()
判断是否为空
-
元视图操作
- Set keySet() 获取关键字的视图set集。键是唯一的。
- Collection values()获取值的视图Connection集,值的集合不是唯一的。
- Set entrySet()
常用的实现类有:HashMap、TreeMap、Properties
实现类HashMap
基于哈希表的Mao接口实现,它是使用频率最高的一个容器,听过所有可选的映射操作,内部对“键”采用三列存放,取值的效率很高,不保证该顺序是恒久不变的。排序是根据哈希算法来排的。
public static void main(String[] args) {
HashMap hashmap = new HashMap();
hash.put("0", "c");
hash.put("1", "a");
hash.put("2", "b");
hash.put("3", "a");
Set set = hashmap.keySet();
Iterator it = set.iterator();
while (it.hasNext()) {
System.out.println(hashmap.get(iterator.next()) + ";");//取出存放的值
}
}
实现类LinkedHashMap
LinkedHashMap是HashMap的子类,可以按照效率比较高的排列元素,增减。
LinkedHashMap lhmap = new LinkedHashMap();
lhmap.put("0", "xmh");
lhmap.put("1", "xmh");
lhmap.put("2", "xmh");
Set set = lhmap.keySet();//获取全部键
Iterator iterator = set.iterator();
while (ierator.hasNext()) {
System.out.println(lhmap.get(iterator.next()) + ";");
}
双重链接列表。此链接列表定义了迭代顺序,即插入顺序,如果映射中重新插入键,则插入顺序不受影响。
实现类TreeMap
TreeMap内部红黑树结构实现,对键实行排序存放,所以键必须是可排序的。
TreeMap treemap = new TreeMap();
treemap.put("0", "xmh");
treemap.put("2", "xmh");
treemap.put("1", "xmh");
System.out.println(treemap.firstKey());
Set set = treemap.keySet();//获取全部键
Iterator iterator = set.iterator();
while (ierator.hasNext()) {
System.out.println(treemap.get(iterator.next()) + ";");
}
实现类Properties
表示了一个持久性的属性集,可以保存在流中或从流中加载。属性列表都是字符串:字符串的形式。继承与HashTable,即哈希表。
使用时不建议用put、putAll、get这类存取元素方法,应该用setProperties(String key, Stirng value)、getProperties(String key);
//创建一个属性文件, 命名为config.properties ,存放在工程的src目录中。
//文件内容如下:
#key=value
name=spirit
password=abc123
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class Properties {
public static void main(String[] args) {
//获取文件,并读入到流中
InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.properties");
//创建属性集对象
Properties prop = new Properties();
try {
prop.load(is);//从流中加载数据
} catch (IOException e) {
e.printStackTrace();
}
String name = prop.getProperty("name");
String pwd = prop.getProperty("password");
System.out.println(name);
System.out.println(pwd);
}
}