目录
6.2.binarySearch(Object[] arr, Object key)方法查找元素
6.3.copyOfRange(int[ ] original, int from, int to)方法拷贝元素
6.4.fill(Object[ ] a, Object val)方法填充元素
6.5.toString(int[ ] arr)方法把数组转换为字符串
一、概述
- 在程序中可以通过数组来保存多个对象,但在某些情况下无法确定到底需要保存多少个对象,此时数组将不再适用,因为数组的长度不可变。
- JDK中提供了一系列特殊的类,这些类可以存储任意类型的对象,并且长度可变,统称为集合。
- 集合按照其存储结构可以分为两大类,即单列集合Collection和双列集合Map,
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是List和Set。其中,List的特点是元素有序、元素可重复。Set的特点是元素无序并且不可重复。List接口的主要实现类有ArrayList和LinkedList,Set接口的主要实现类有HashSet和TreeSet。
Map:双列集合类的根接口,用于存储具有键(Key)、值(Value)映射关系的元素,每个元素都包含一对键值,在使用Map集合时可以通过指定的Key找到对应的Value,例如根据一个学生的学号就可以找到对应的学生。Map接口的主要实现类有HashMap和TreeMap。
二、Collection接口
Collection是所有单列集合的父接口,因此在Collection中定义了单列集合(List和Set)通用的一些方法,这些方法可用于操作所有的单列集合。
1.List接口
- List接口继承自Collection接口,是单列集合的一个重要分支,习惯性地会将实现了List接口的对象称为List集合。
- 在List集合中允许出现重复的元素,所有的元素是以一种线性方式进行存储的,在程序中可以通过索引来访问集合中的指定元素。
- 另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致。
- List不但继承了Collection接口中的全部方法,而且还增加了一些根据元素索引来操作集合的特有方法。
1.1.ArrayList集合
- ArrayList是List接口的一个实现类,它是程序中最常见的一种集合
- 在ArrayList内部封装了一个长度可变的数组对象,当存入的元素超过数组长度时,ArrayList会在内存中分配一个更大的数组来存储这些元素,因此可以将ArrayList集合看作一个长度可变的数组
- ArrayList集合中大部分方法都是从父类Collection和List继承过来的,其中add()方法和get()方法用于实现元素的存取
public class Test_ArrayList {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 创建ArrayList集合
list.add("Hello");
list.add("World");
list.add("!");
System.out.println("集合中的元素个数:"+list.size()); // 集合的元素个数
System.out.println("第2个元素是:"+list.get(1)); // 获取集合中的某个元素
System.out.println("集合元素:"+list);
}
}
1.2.LinkedList集合
- List接口的另一个实现类LinkedList,克服了ArrayList集合在查询元素时速度很快,但在增删元素时效率较低的局限性。
- 该集合内部维护了一个双向循环链表,链表中的每一个元素都使用引用的方式来记住它的前一个元素和后一个元素,从而可以将所有的元素彼此连接起来。
- 当插入一个新元素时,只需要修改元素之间的这种引用关系即可,删除一个节点也是如此。
- 添加元素和删除元素示意图:
LinkedList集合除了具备增删元素效率高的特点,还专门针对元素的增删操作定义了一些特有的方法。
public class Test_LinkListed {
public static void main(String[] args) {
LinkedList link = new LinkedList(); // 创建LinkedList集合
link.add("I");
link.add(" am");
link.add(" a");
link.add(" student");
System.out.println("集合中的元素:"+link.toString());// 以字符串的形式打印集合中的元素
// 在集合指定位置插入元素
link.add(3," good");
System.out.println("集合中的元素:"+link);
// 在集合的第一个位置插入元素
link.addFirst("Hey ");
System.out.println("集合中的元素:"+link);
// 取集合的第一个元素
System.out.println("集合中的第一个元素:"+link.getFirst());
// 移除集合的第一个元素
link.removeFirst();
System.out.println("集合中的元素:"+link);
}
}
1.3.Iterator集合
- Iterator接口也是Java集合框架中的一员,但它与Collection、Map接口有所不同,Collection接口与Map接口主要用于存储元素,而Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Iterator对象也被称为迭代器。
- Iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素,为了让初学者能更好地理解迭代器的工作原理,通过下图例演示Iterator对象迭代元素的过程:
public class Test_Iterator {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("You");
list.add(" are");
list.add(" a");
list.add(" good");
list.add(" person.");
Iterator it = list.iterator();
while(it.hasNext()) { // 判断集合是否存在下一元素
Object obj = it.next(); // 获取集合元素
System.out.print(obj);
}
/*
注意:
当通过迭代器获取ArrayList集合中的元素时,
都会将这些元素当做Objct类型来看待,如果想得到特定的元素类型,
都需要进行强制类型转换
*/
}
}
1.3.1.Iterator的局限性
使用Iterator迭代器对集合中的元素进行迭代时,如果调用了集合对象的remove()方法去删除元素,会出现异常。
假设用一个名单存储了所有员工的姓名,期间一位叫wangwu的员工离职了,我们需要将他从名单上移除。
public class Test_IteratorError {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("tianqi");
list.add("wangba");
Iterator it = list.iterator();
while(it.hasNext()) {
Object obj = it.next();
if("wangwu".equals(obj)) {
list.remove(obj); // 删除该集合中的元素
}
}
System.out.println(list);
}
}
运行时出现了并发修改异常ConcurrentModificationException。这个异常是迭代器对象抛出的,出现异常的原因是集合中删除了元素会导致迭代器预期的迭代次数发生改变,导致迭代器的结果不准确。
1.3.2.解决方案<1>
从逻辑上只想把姓名为wangwu的员工删除,至于后面还有多少员工我们并不关心,所以只需找到该员工后跳出循环不再迭代即可,也就是增加一个break语句,代码如下:
public class Test_IteratorError {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("tianqi");
list.add("wangba");
Iterator it = list.iterator();
// 第一种方式:
while(it.hasNext()) {
Object obj = it.next();
if("wangwu".equals(obj)) {
list.remove(obj); // 删除该集合中的元素
break; // 结束循环
}
}
System.out.println(list);
}
}
1.3.3.解决方案<2>
如果需要在集合的迭代期间对集合中的元素进行删除,可以使用迭代器本身的删除方法,将删除代码替换成it.remove()即可解决这个问题,代码如下:
public class Test_IteratorError {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("tianqi");
list.add("wangba");
Iterator it = list.iterator();
// 第二种方式:
while(it.hasNext()) {
Object obj = it.next();
if("wangwu".equals(obj)) {
it.remove(); // 删除该集合中的元素
}
}
System.out.println(list);
}
}
1.4.foreach循环(JDK5.0新特性)
foreach循环不需要获得集合的元素个数,也不需要根据索引访问集合中的元素,但它会自动遍历集合中的每个元素。
语法格式:
for(集合元素类型 临时变量 : 集合对象){
执行代码......
}
public class Test_Foreach {
public static void main(String[] args) {
ArrayList list = new ArrayList(); // 创建集合
list.add("Today");
list.add(" is");
list.add(" Saturday");
for(Object obj : list) {
System.out.print(obj);
}
}
}
1.4.1.foreach循环的局限性
foreach循环虽然书写起来很简洁,但在使用时也存在一定的局限性。当使用foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改,如下程序示例所示:
public class Test_Comparable {
// 创建一个静态数组
static String[] names = {"zhangsan","lisi","wangwu"};
public static void main(String[] args) {
// foreach循环修改并遍历数组
for(String name : names) {
name = "二驴子";
}
System.out.print("foreach循环遍历数组:");
System.out.println(names[0]+" "+names[1]+" "+names[2]);
//for循环遍历数组
for(int i = 0; i<names.length; ++i) {
names[i] = "二驴子";
}
System.out.print("for循环遍历数组:");
System.out.println(names[0]+" "+names[1]+" "+names[2]);
/*
综上可知:
当使用foreach循环遍历集合和数组时,只能访问集合中的元素,
不能对集合中的元素进行修改
*/
}
}
1.5.ListIterator接口
- Iterator迭代器提供了hasNext()方法和next()方法,通过这两个方法可以实现集合中元素的迭代,迭代的方向是从集合中的第一个元素向最后一个元素迭代,也就是所谓的正向迭代。
- 为了使迭代方式更加多元化,JDK中还定义了一个ListIterator迭代器,它是Iterator的子类,该类在父类的基础上增加了一些特有方法。
public class Test_ListIterator {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("zhangsan");
list.add("lisi");
list.add("wangwu");
list.add("zhaoliu");
list.add("liangqi");
list.add("wangba");
ListIterator it = list.listIterator(list.size());
while(it.hasPrevious()) {
Object obj = it.previous();
System.out.print(obj+" ");
}
/*
注意:
获取ListIterator对象时,最好加上集合的元素个数
否则将无法进行遍历
*/
}
}
1.6.Enumeration接口
- JDK1.2之前还没有Iterator接口的时候,遍历集合需要使用Enumeration接口,它的用法和Iterator类似。
- JDK中提供了一个Vevtor集合,该集合是List接口的一个实现类,用法与ArrayList完全相同,区别在于Vector集合是线程安全的,而ArrayList集合是线程不安全的。在Vector类中提供了一个elements()方法用于返回Enumeration对象,通过Enumeration对象就可以遍历该集合中的元素。
public class Test_Enumeration {
public static void main(String[] args) {
Vector v = new Vector();
v.add("zhangsan");
v.add("lisi");
v.add("wangwu");
v.add("zhaoliu");
v.add("liangqi");
v.add("wangba");
Enumeration en = v.elements(); // 获取Enumeration对象
while(en.hasMoreElements()) { // 判断该对象是否有更多元素
Object obj = en.nextElement();
System.out.print(obj+" ");
}
}
}
2.set接口
Set接口和List接口一样,同样继承自Collection接口,它与Collection接口中的方法基本一致,并没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格了。与List接口不同的是,Set接口中元素无序,并且都会以某种规则保证存入的元素不出现重复。
Set接口主要有两个实现类,分别是HashSet和TreeSet。其中,HashSet是根据对象的哈希值来确定元素在集合中的存储的位置,因此具有良好的存取和查找性能。TreeSet则是以二叉树的方式来存储元素,它可以实现对集合中的元素进行排序。
2.1.HashSet
HashSet是Set接口的一个实现类,它所存储的元素是不可重复的,并且元素都是无序的。当向HashSet集合中添加一个对象时,首先会调用该对象的hashCode()方法来确定元素的存储位置,然后再调用对象的equals()方法来确保该位置没有重复元素。
public class Test_HashSet_1 {
public static void main(String[] args) {
HashSet set = new HashSet(); // 创建HashSet集合
set.add("zhangsan");
set.add("lisi");
set.add("wangwu");
set.add("wangwu"); // 向Set集合中添加重复元素
Iterator it = set.iterator();
while(it.hasNext()) { // while循环判断集合中是否有元素
Object obj = it.next(); // 获取当前集合中的元素
System.out.print(obj+" ");
}
}
}
HashSet集合之所以能确保不出现重复的元素,是因为它在存入元素时做了很多工作。当调用HashSet集合的add()方法存入元素时,首先调用当前存入对象的hashCode()方法获得对象的哈希值,然后根据对象的哈希值计算出一个存储位置。如果该位置上没有元素,则直接将元素存入,
如果该位置上有元素存在,则会调用equals()方法让当前存入的元素依次和该位置上的元素进行比较,如果返回的结果为false就将该元素存入集合,返回的结果为true则说明有重复元素,就将该元素舍弃。
当向集合中存入元素时,为了保证HasheSet正常工作,要求在存入对象时,需要重写Object类中的hashCode()和equals()方法。如下示例将字符串存入HashSet时,String类已经重写了hashCode ()和equals()方法。但是如果将Student对象存入HashSet,结果如何?
public class Test_HashSet_2 {
public static void main(String[] args) {
Student st1 = new Student("1","zhangsan");
Student st2 = new Student("2","lisi");
Student st3 = new Student("3","wangwu");
HashSet set = new HashSet();
set.add(st1);
set.add(st2);
set.add(st3);
System.out.println(set);
}
}
class Student{
private String id;
private String name;
public Student(String id, String name) {
this.id = id;
this.name = name;
}
public String toString() { // 重写toString方法
return id + ":" + name;
}
}
public class Test_HashSet_3 {
public static void main(String[] args) {
Students st1 = new Students("1","zhangsan");
Students st2 = new Students("2","wangwu");
Students st3 = new Students("2","wangwu");
HashSet set = new HashSet();
set.add(st1);
set.add(st2);
set.add(st3);
System.out.println(set);
}
}
class Students{
private String id;
private String name;
public Students(String id, String name) {
this.id = id;
this.name = name;
}
// 重写toString方法
public String toString() {
return id + ":" + name;
}
// 重写HashCode方法
public int hashCode() {
return id.hashCode(); // 返回id属性的哈希值
}
// 重写equals方法
public boolean equals(Object obj) {
if(this == obj) { // 判断是否是同一对象
return true;
}
if(!(obj instanceof Students
)) { // 判断对象是否是Students类型
return false;
}
Students st = (Students)obj; // 强制转化为Students类型
boolean res = this.id.equals(st.id);
return res;
}
}
2.2.TreeSet
- TreeSet是Set接口的另一个实现类,它内部采用平衡二叉树来存储元素,这样的结构可以保证TreeSet集合中没有重复的元素,并且可以对元素进行排序。
- 所谓二叉树就是说每个节点最多有两个子节点的有序树,每个节点及其子节点组成的树称为子树,通常左侧的子节点称为“左子树”,右侧的节点称为“右子树”,其中左子树上的元素应小于它的根结点,而右子树上的元素应大于它的根结点。
- 假设向集合中存入8个元素,依次为13、8、17、17、1、11、15、25,如果以二叉树的方式来存储,在集合中的存储结构会形成一个树状结构。
- 二叉树中元素的存储过程:
当二叉树中存入新元素时,新元素首先会与第 1 个元素 ( 最顶层元素 ) 进行比较,如果小于第 1 个元素就执行左边的分支,继续和该分支的子元素进行比较。如果大于第 1 个元素就执行右边的分支,继续和该分支的子元素进行比较。如此往复,直到与最后一个元素进行比较时,如果新元素小于最后一个元素就将其放在最后一个元素的左子树上,如果大于最后一个元素就将其放在最后一个元素的右子树上。
public class Test_TreeSet_1 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add("zhangsan");
ts.add("li");
ts.add("wangwu");
Iterator it = ts.iterator();
while(it.hasNext()) {
System.out.print(it.next()+" ");
}
}
}
在TreeSet集合中存放Student类型对象时,如果Student类没有实现Comparable接口,则Student类型的对象将不能进行比较,这时,TreeSet集合就不知道按照什么排序规则对Student对象进行排序,最终导致程序报错。因此,为了在TreeSet集合中存放Student对象,必须使Student类实现Comparable接口。
public class Test_TreeSet_2 {
public static void main(String[] args) {
TreeSet ts = new TreeSet();
ts.add(new Student(2,"zhangsan"));
ts.add(new Student(2,"lisi"));
ts.add(new Student(1,"wangwu"));
ts.add(new Student(1,"wangwu"));
Iterator it = ts.iterator();
while(it.hasNext()) {
System.out.print(it.next()+" ");
}
}
}
class Student implements Comparable{
private int id;
private String name;
public Student(int id, String name) {
this.id = id;
this.name = name;
}
// 重写toString方法
public String toString() {
return id + ":" + name;
}
// 重写Comparable的ComparaTo方法
public int compareTo(Object obj) {
Student st = (Student)obj; // 将比较对象强转为Student类型
if(this.id - st.id > 0) { // id不同的情况
return 1;
}
if(this.id - st.id == 0) {// id相同的情况
return this.name.compareTo(st.name);
}
return -1; // id相同、名字相同的情况
}
}
定义的类没有实现Comparable接口或者对于实现了Comparable接口的类而不想按照定义的compareTo()方法进行排序,,例如,希望字符串可以按照长度来进行排序,这时,可以通过自定义比较器的方式对TreeSet集合中的元素排序,即实现Comparator接口,在创建TreeSet集合时指定比较器。
接下来实现TreeSet集合中字符串按照长度进行排序。
public class Test_TreeSet_3 {
public static void main(String[] args) {
TreeSet ts = new TreeSet(new MyComparator()); // 创建对象时传入自定义比较器
ts.add("zhangsan");
ts.add("lisi");
ts.add("wangwu");
Iterator it = ts.iterator();
while(it.hasNext()) {
System.out.print(it.next()+" ");
}
}
}
class MyComparator implements Comparator{
public int compare(Object obj1, Object obj2) {
String str1 = (String)obj1;
String str2 = (String)obj2;
int temp = str1.length() - str2.length();// 根据字符串长度进行比较
return temp;
}
}
三、Map接口
- 在现实生活中,每个人都有唯一的身份证号,通过身份证号可以查询到这个人的信息,这两者是一对一的关系。
- 在应用程序中,如果想存储这种具有对应关系的数据,则需要使用JDK中提供的Map接口。
- Map接口是一种双列集合,它的每个元素都包含一个键对象Key和值对象Value,键和值对象之间存在一种对应关系,称为映射。
- 从Map集合中访问元素时,只要指定了Key,就能找到对应的Value。
- Map接口中定义的一些方法:
3.1.HashMap集合
HashMap集合是Map接口的一个实现类,它用于存储键值映射关系,但必须保证不出现重复的键。
public class Test_Hashmap_1 {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
HashMap hm = new HashMap(); // 创建map对象
hm.put("1", "zhangsan");
hm.put("2", "lisi");
hm.put("3", "wangwu");
System.out.println("1:"+hm.get("1"));
System.out.println("2:"+hm.get("2"));
System.out.println("3:"+hm.get("3"));
}
}
public class Test_Hashmap_2 {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
HashMap hm = new HashMap(); // 创建map对象
hm.put("1", "zhangsan");
hm.put("2", "lisi");
hm.put("3", "wangwu");
hm.put("3", "zhaoliu"); // 键唯一,重复添加会将旧值替换成新值
System.out.println("1:"+hm.get("1"));
System.out.println("2:"+hm.get("2"));
System.out.println("3:"+hm.get("3"));
}
}
在程序开发中,经常需要取出Map中所有的键和值,那么如何遍历Map中所有的键值对呢?
有两种方式可以实现,第一种方式就是先遍历Map集合中所有的键,再根据键获取相应的值。
public class Test_HashMap_2 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
HashMap hm = new HashMap(); // 创个HashMap集合
hm.put("1", "zhangsan"); // 存储键和值
hm.put("2", "lisi");
hm.put("3", "wangwu");
Set keySet = hm.keySet(); // 获取键的集合
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next(); // 获取键
Object value = hm.get(key);
System.out.println(key + ":" + value);
}
}
}
另外一种遍历方式是先获取集合中的所有的映射关系,然后从映射关系中取出键和值。
public class Test_HashMap_3 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
HashMap hm = new HashMap(); // 创建map对象
hm.put("1", "zhangsan");
hm.put("2", "lisi");
hm.put("3", "wangwu");
Set entrySet = hm.entrySet(); // 获取集合中的所有映射关系
Iterator it = entrySet.iterator();
while(it.hasNext()) {
HashMap.Entry entry = (HashMap.Entry)(it.next()); // 获取映射关系中的"键值对"映射关系
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + ":" + value);
}
}
在Map中,还提供了一个values()方法,通过这个方法可以直接获取Map中存储所有值的Collection集合。
public class Test_HashMap_4 {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void main(String[] args) {
HashMap hm = new HashMap(); // 创建HashMap集合
hm.put("1", "zhangsan"); // 存储键和值
hm.put("2", "lisi");
hm.put("3", "wangwu");
Collection values = hm.values(); // 获取值的集合
Iterator it = values.iterator();
while(it.hasNext()) {
System.out.print(it.next()+" ");
}
}
}
3.2.LinkedHashMap
Java中提供的LinkedHashMap类是HashMap的子类,和LinkedList一样也使用双向链表来维护内部元素的关系,使Map元素迭代的顺序与存入的顺序一致。
public class Test_LinkedHashMap {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
LinkedHashMap lhm = new LinkedHashMap();
lhm.put("1","zhangsan");
lhm.put("2","lisi");
lhm.put("3","wangwu");
Set keySet = lhm.keySet(); // 获取键的集合
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = lhm.get(key);
System.out.println(key + ":" + value);
}
}
}
3.3.TreeMap集合
TreeMap集合是用来存储键值映射关系的,其中不允许出现重复的键。在TreeMap中是通过二叉树的原理来保证键的唯一性,这个TreeSet集合存储的原理一样,因此TreeMap中所有的键是按照某种顺序排列的。
public class Test_TreeMap_1 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
TreeMap tm = new TreeMap();
tm.put("1","zhangsan");
tm.put("3","wangwu");
tm.put("2","lisi");
tm.put("3","wangwu");
Set keySet = tm.keySet(); // 获取键的集合
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = tm.get(key);
System.out.println(key + ":" + value);
}
}
}
在使用TreeMap集合时,也可以通过自定义比较器的方式对所有的键进行排序
public class Test_TreeMap_2 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
TreeMap tm = new TreeMap(new MyComparator()); // 创建TreeMap集合时传入自定义比较器
tm.put("1","zhangsan");
tm.put("2","lisi");
tm.put("3","wangwu");
Set keySet = tm.keySet(); // 获取键的集合
Iterator it = keySet.iterator();
while(it.hasNext()) {
Object key = it.next();
Object value = tm.get(key);
System.out.println(key + ":" + value);
}
}
}
@SuppressWarnings("rawtypes")
class MyComparator implements Comparator{ // 自定义比较器
public int compare(Object o1, Object o2) { // 对键进行比较
String id1 = (String)o1;
String id2 = (String)o2;
return id2.compareTo(id1); // 返回比较后的值
}
}
3.4.Hashtable集合
Map接口中还有一个实现类Hashtable,它在存取元素时速度很慢,目前基本上被HashMap类所取代。
但Hashtable类有一个子类Properties在实际应用中非常重要,Properties主要用来存储字符串类型的键和值,在实际开发中,经常使用Properties集合来存取应用的配置项。
假设有一个文本编辑工具,要求默认字体颜色是蓝色,字体大小为30px,语言为中文,代码如下:
public class Test_Propertise_1 {
@SuppressWarnings("rawtypes")
public static void main(String[] args) {
Properties p = new Properties(); // 创建Propertise对象
p.setProperty("color", "blue");
p.setProperty("Font-size", "30px");
p.setProperty("Language", "chinese");
Set keySet = p.keySet(); // 获取集合中所有键
Iterator it = keySet.iterator();
while(it.hasNext()) {
String key = (String)it.next();
String value = p.getProperty(key);
System.out.println(key + " = " + value);
}
}
}
四、泛型
4.1.何谓泛型
当把一个对象存入集合后,集合会“忘记”这个对象的类型,将该对象从集合中取出时,这个对象的编译类型就变成了Object类型。
换句话说,我们在程序中无法确定一个集合中的元素到底是什么类型的。那么在取出元素时,如果进行强制类型转换就很容易出错。
public class Test_Exam_1 {
@SuppressWarnings({ })
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("zhangsan");
list.add(18);
for(Object obj : list) {
String str = (String)obj;
}
}
}
为了解决这个问题,在Java中引入了“参数化类型(parameterized type)”这个概念,即泛型。
它可以限定方法操作的数据类型,在定义集合类时,可以使用“<参数化类型>”的方式指定该类中方法操作的数据类型。
代码如下:
public class Test_Exam_1 {
@SuppressWarnings({ })
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(); // 指定类中操作的数据类型
list.add("zhangsan");
list.add(18);
for(String str : list) {
System.out.println(str);
}
}
}
程序编译报错的原因是修改后的代码限定了集合元素的数据类型,ArrayList<String>这样的集合只能存储String类型的元素,程序在编译时,编译器检查出Integer类型的元素与List集合的规定的类型不匹配,编译不通过,这样就可以在编译时期解决错误,避免程序在运行时期发生错误。
正确代码如下:
public class Test_Exam_1 {
@SuppressWarnings({ })
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>(); // 指定类中操作的数据类型
list.add("zhangsan");
list.add("18");
// list.add(18);
for(String str : list) {
System.out.println(str);
}
}
}
4.2.自定义泛型
public class Test_Exam_2 {
@SuppressWarnings("removal")
public static void main(String[] args) {
CachePool pool = new CachePool(); // 创建CachePool对象
pool.save(new Integer(1)); // 存入Integer类型的数据
String temp = pool.get();
System.out.println(temp);
}
}
class CachePool{
private Object temp;
public void save(Object temp) {
this.temp = temp;
}
public Object get() {
return temp;
}
}
从运行结果可以看出,程序在编译时期就报错,这是因为存入了一个Integer类型的数据,在取出这个数据时,将该数据转换成了String类型,出现了类型不匹配的错误。
为了避免这个问题,就可以使用泛型,如果在定义一个类CachePool时使用<T>声明参数类型,(T其实就是Type的缩写,这里也可以使用其它字符,为了方便理解都定义为T),将save()方法的参数类型和get()方法的返回值类型都声明为T,那么在存入元素时元素的类型就被限定了,容器中就只能存入这种T类型的元素,在取出元素时就无需进行类型转换。
public class Test_Exam_3 {
@SuppressWarnings("removal")
public static void main(String[] args) {
CachePools<Integer> pool = new CachePools<Integer>(); // 指定传入类型为Integer类型
pool.save(new Integer(99)); // 存入Integer类型的数据
Integer temp = pool.get();
System.out.println("temp = " + temp);
}
}
class CachePools<T>{ // 在创建类时,声明参数类型为T
private T temp;
public void save(T temp) {
this.temp = temp;
}
public T get() {
return temp;
}
}
五、Collections工具
5.1.Collections的作用
- JDK提供了一个工具类专门用来操作集合,这个类就是Collections,它位于java.util包中。Collections类中提供了大量的方法用于对集合中元素进行排序、查找和修改等操作,接下来对这些常用的方法进行介绍。
- Collections类中提供了一系列方法用于对List集合进行排序;
public class Test_Collections_1 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
ArrayList list = new ArrayList();
Collections.addAll(list, 5,4,22,69,3,55); // 添加元素
System.out.println("排序前:"+list);
Collections.swap(list, 3, 5);
System.out.println("元素交换后:"+list);
Collections.reverse(list);
System.out.println("反转后:"+list);
Collections.sort(list);
System.out.println("自然排序后:"+list);
Collections.shuffle(list);
System.out.println("洗牌后:"+list);
}
}
5.2.Collections的副作用
Collections类还提供了一些常用方法用于查找、替换集合中的元素
public class Test_Collections_2 {
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void main(String[] args) {
ArrayList list = new ArrayList();
Collections.addAll(list, 22,3,12,65,100,34,0); // 添加元素
System.out.println("集合中的元素:"+list);
System.out.println("集合中的最大元素:"+Collections.max(list));
System.out.println("集合中的最小元素:"+Collections.min(list));
Collections.replaceAll(list, 100, 99); // 用99替换掉集合中的100
System.out.println("替换后的集合中的元素:"+list);
}
}
六、Arrays工具
java.util包中还提供了一个专门用于操作数组的工具类——Arrays。Arrays工具类提供了大量的静态方法。
6.1.sort()方法排序
在学习数组时,要想对数组进行排序就需要自定义一个排序方法,其实也可以使用Arrays工具类中的静态方法sort()来实现这个功能,接下来通过一个案例来学习sort()方法的使用。
public class Test_Arrays_1 {
public static void main(String[] args) {
int[] arr = {5,2,3,4,6,8,9,1,7};
System.out.print("排序前:");
PrintArray(arr);
Arrays.sort(arr);
System.out.print("排序后:");
PrintArray(arr);
}
// 创建一个静态的打印方法
public static void PrintArray(int[] arr) {
System.out.print("[");
for(int i = 0; i<arr.length; ++i) {
System.out.print(arr[i] + ",");
}
System.out.println("]");
}
}
6.2.binarySearch(Object[] arr, Object key)方法查找元素
程序开发中,经常会在数组中查找某些特定的元素,如果数组中元素较多时查找某个元素就会非常繁琐,为此,Arrays类中提供还了一个方法binarySearch(Object[] a, Object key)用于查找元素.
public class Test_Arrays_2 {
public static void main(String[] args) {
int[] arr = {1,3,1,4};
Arrays.sort(arr); // 查找前先排序[1,1,3,4]
int index = Arrays.binarySearch(arr, 3);
System.out.println("元素3的索引是;"+index);
}
}
所谓二分法查找就是每次将指定元素和数组中间位置的元素进行比较,从而排除掉其中的一半元素,这样的查找是非常高效的。接下来通过一个图例来演示二分法查找元素的过程。
6.3.copyOfRange(int[ ] original, int from, int to)方法拷贝元素
在程序开发中,经常需要在不破坏原数组的情况下使用数组中的部分元素,这时可以使用Arrays工具类的copyOfRange(int[] original,int from,int to)方法将数组中指定范围的元素复制到一个新的数组中,该方法中参数original表示被复制的数组,from表示被复制元素的初始索引(包括),to表示被复制元素的最后索引(不包括)。
public class Test_Arrays_3 {
public static void main(String[] args) {
int[] arr = {1,4,7,2,5,8};
int[] copy = Arrays.copyOfRange(arr, 3, 8);
System.out.print("拷贝后的元素:");
for(int copys : copy) {
System.out.print(copys+" ");
}
}
}
6.4.fill(Object[ ] a, Object val)方法填充元素
程序开发中,经常需要用一个值替换数组中的所有元素,这时可以使用Array的fill(Object[ ] a, Object val)方法,该方法可以将指定的值赋给数组中的每一个元素。
public class Test_Arrays_4 {
public static void main(String[] args) {
int[] arr = {2,4,6,8};
System.out.print("填充前的元素:");
Print(arr);
Arrays.fill(arr, 8);
System.out.print("填充后的元素:");
Print(arr);
}
public static void Print(int[] arr) {
for(int elements : arr) {
System.out.print(elements+" ");
}
System.out.println();
}
}
6.5.toString(int[ ] arr)方法把数组转换为字符串
在程序开发中,经常需要把数组以字符串的形式输出,这时就可以使用Arrays工具类的另一个方法toString(int[ ] arr)。需要注意的是,该方法并不是对Object类toString()方法的重写,只是用于返回指定数组的字符串形式。
public class Test_Arrays_5 {
public static void main(String[] args) {
int[] arr = {2,4,6,8,10};
String str = Arrays.toString(arr); // 把数组转换成字符串
System.out.println("数组转换成字符串后:"+str);
}
}