在java.util包下提供了两个核心接口用于处理集合类:Collection接口、Map接口。本文章主要介绍集合类中关于Collection接口的相关集合类以及集合的4种标准输出方式。
1. Collection接口
public interface Collection<E> extends Iterable<E>
该接口定义了如下方法:
(1)向集合中添加数据:
boolean add(E e);
(2)向集合中添加一组数据:
boolean addAll(Collection<? extends E> c);
(3)清空集合数据:
void clear();
(4)查找数据是否存在(需要使用equals()方法):
boolean contains(Object o);
(5)删除数据(需要使用equals()方法):
boolean remove(Object o);
(6)取得集合大小:
int size();
(7)将集合变成对象数组返回:
Object[] toArray();
(8)取得Iterable接口对象,用于集合输出:
Iterator<E> iterator();
Collection接口下有两个子接口List接口、Set接口。下面一一介绍这两个接口。
2. Collection接口的子接口------List接口
List接口在java.util包下,而在java.awt包下有一个List类用于界面设计,和java.util包下的List接口没有任何关系。List接口中有两个比较重要的扩充方法:
(1)根据索引取得数据:
E get(int index);
(2)根据索引修改数据:
E set(int index, E element);
由于List本身也是接口,要想取得其实例化对象就必须有子类,List接口下有常用的三个子类:ArrayList、Vector、LinkedList类。下面一一介绍:
2.1 实现List接口的子类------ArrayList类
ArrayList类是针对List接口的数组实现的,故ArrayList类的底层是数组。下面观察利用ArrayList类对List接口进行的基本操作:
package www.bit.java.test; import java.util.ArrayList; import java.util.List; public class Test3 { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("数据1"); list.add("数据2"); list.add("数据2"); System.out.println(list); } }
运行结果:
[数据1, 数据2, 数据2]
通过代码的运行结果可以看出,List接口允许保存重复数据。
再来观察get()方法和set()方法的调用:
package www.bit.java.test; import java.util.ArrayList; import java.util.List; public class Test3 { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("数据1"); list.add("数据2"); list.add("数据2"); //调用set()方法,修改指定索引的元素 System.out.println(list.set(2,"数据3")); //调用get()方法,取得指定索引的元素 for(int i=0;i<list.size();i++) { System.out.println(list.get(i)); } } }
运行结果:
数据2 数据1 数据2 数据3
通过代码以及运行结果可以看出,set()方法返回的是修改指定索引前的元素值,还需要注意的是,get()方法和set()方法是List接口特有的方法,Collection接口没有定义这两个方法。
在开发中,集合里面一般保存的数据类型不是基本数据类型而是简单Java类,下面观察向集合中保存简单Java类对象的操作:
package www.bit.java.work; import java.util.ArrayList; import java.util.List; import java.util.Objects; class Person{//简单Java private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Preson [name=" + name + ", age=" + age + "]"; } } public class Test5 { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",20)); list.add(new Person("李四",21)); list.add(new Person("王麻子",31)); System.out.println(list.contains(new Person("李四",21))); System.out.println(list.remove(new Person("王麻子",31))); System.out.println(list); } }
运行结果如下:
false false [Preson [name=張三, age=20], Preson [name=李四, age=21], Preson [name=王麻子, age=31]]
通过代码以及运行结果可以看出,调用contiains()方法并没有找到对应的数据,调用remove()方法并没有删除对应的数据,这是因为什么?其实是因为new Person()是在堆上保存着其引用,每次调用new出来的对象都不一样,故其引用也不相同,所以会出现没有找到以及没删除指定数据的现象,要是删除指定的数据需要覆写equals()方法,因为集合操作简单Java类时,对于contains()和remove()方法需要equals()方法的支持。
修改以上代码,覆写equals()方法后再观察运行结果:
package www.bit.java.work; import java.util.ArrayList; import java.util.List; import java.util.Objects; class Person{//简单Java private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Preson [name=" + name + ", age=" + age + "]"; } @Override public boolean equals(Object obj) { if(obj==this) { return true; } if(obj==null||obj.getClass()!=this.getClass()) { return false; } Person person=(Person)obj; return Objects.equals(this.name,person.getName()) &&Objects.equals(this.age,person.getAge()); } } public class Test5 { public static void main(String[] args) { List<Person> list = new ArrayList<>(); list.add(new Person("張三",20)); list.add(new Person("李四",21)); list.add(new Person("王麻子",31)); System.out.println(list.contains(new Person("李四",21))); System.out.println(list.remove(new Person("王麻子",31))); System.out.println(list); } }
运行结果:
true true [Preson [name=張三, age=20], Preson [name=李四, age=21]]
此时,通过运行结果可以看出,覆写了equals()方法后能找到指定的数据以及能删除指定的数据。
2.2 实现List接口的子类------Vector类
Vector类是从JDK1.0时提出的,而ArrayList接口时从JDK1.2时提出的。Vector类的底层也是数组。使用Vector类实例化List接口的对象后,其他使用的方式与ArrayList一样,下面通过Vector类实例化List接口对象进行相关操作:
package www.bit.java.work; import java.util.List; import java.util.Objects; import java.util.Vector; public class Test5 { public static void main(String[] args) { List<String> list = new Vector<>(); list.add("数据1"); list.add("数据2"); list.add("数据2"); list.add("数据3"); System.out.println(list); } }
运行结果如下:
[数据1, 数据2, 数据2, 数据3]
那么,既然ArrayList类与Vector类在使用上基本一致,那么ArrayList类与Vector类的区别在哪儿呢?主要从四个方面介绍它们之间的区别:
(1)提出时间:ArrayList是从JDK1.2提出的;Vector是从JDK1.0提出的。
(2)处理形式:ArrayList是异步处理,性能高;Vector是同步处理,性能低。
(3)数据安全:ArrayList是线程不安全;Vector是线程安全。
(4)输出形式:ArrayList支持Iterator、ListIterator、foreach;Vector支持Iterator、ListIterator、foreach、Enumeration。
2.3 List接口的子类------LinkedList类
LinkedList类与ArrayList类、Vector类的区别是LinkedList类的底层是用链表实现的,而ArrayList类、Vector类的底层是用数组实现的。在使用方面与ArrayList类、Vector类没有任何区别。
下面通过LinkedList类实例化List接口对象,并进行相关操作:
package www.bit.java.work; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Vector; public class Test5 { public static void main(String[] args) { List<String> list = new LinkedList<>(); list.add("数据1"); list.add("数据2"); list.add("数据2"); list.add("数据3"); System.out.println(list); } }
运行结果:
[数据1, 数据2, 数据2, 数据3]
3. Collection接口的子接口------Set接口
Collection接口的子接口List接口允许保存重复的数据,而Set接口不允许保存重复的数据。同List接口一样,既然是接口没法实例化其对象,就需要该接口的子类去实例化对象。实现Set接口的子类有:HashSet类、TreeSet类。下面一一介绍这两个子类:
3.1 实现Set接口的子类------HashSet类
HashSet类的底层是由链表+红黑树实现的,集合中的数据若为基本数据类型则是有序存储,其他数据类型是无序存储的并且HashSet允许数据为null。下面通过HashSet子类实例化Set接口对象并进行集合的相关操作:
package www.bit.java.work; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.TreeSet; import java.util.Vector; public class Test5 { public static void main(String[] args) { Set<String> set = new HashSet<>(); set.add("C"); set.add("A"); set.add("D"); set.add("B"); set.add("A"); set.add(null); System.out.println(set); } }
运行结果:
[null, A, B, C, D]
3.2 实现Set接口的子类------TreeSet类
TreeSet类的底层是由红黑树实现的,集合中的数据是有序存储的但是TreeSet不允许数据为null。下面通过TreeSet子类实例化Set接口对象并进行集合的相关操作:
package www.bit.java.work; import java.util.Set; import java.util.TreeSet; public class Test5 { public static void main(String[] args) { Set<String> set = new TreeSet<>(); set.add("C"); set.add("A"); set.add("D"); set.add("B"); set.add("A"); System.out.println(set); } }
运行结果如下:
[A, B, C, D]
3.3 关于TreeSet有序存储的分析
既然TreeSet子类可以对结合进行有序存储,那么我们可以利用其特性实现数据的排序处理操作。此时,要想实现集合的有序存储,实际上是对数据对象的排序,那么就要求对象所在类一定要实现Comparable接口并且覆写compareTo()方法。在TreeSet类的操作中,我们存储的对象为String类对象从而在运行结果中可以看出集合是有序存储,实际上是因为String类实现了Comparable接口并且覆写了compareTo()方法。
下面存储简单Java类对象并在其所在类中实现Comparable接口以及覆写compareTo()方法,从而对该集合进行有序存储。
package www.bit.java.work; import java.util.Set; import java.util.TreeSet; class Person implements Comparable<Person>{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public int compareTo(Person person) { if(this.age>person.age) { return 1; }else if(this.age<person.age) { return -1; }else { return this.name.compareTo(person.name); } } } public class Test5 { public static void main(String[] args) { Set<Person> set=new TreeSet<>(); set.add(new Person("张三",11)); set.add(new Person("张三",10)); set.add(new Person("李四",11)); set.add(new Person("张三",12)); System.out.println(set); } }
运行结果如下:
[Person [name=张三, age=19], Person [name=李四, age=20], Person [name=王麻子, age=20], Person [name=张三, age=21]
在实际的使用中,由于使用TreeSet类过于麻烦需要实现Comparable并覆写compareTo()方法,所以一般使用的是HashSet类。
3.4 重复元素的判断
由于Set接口不允许出现重复元素,那么Set接口是怎么判断重复元素的呢?在使用TreeSet子类进行数据的保存时,重复元素的判断是通过实现Comaprable接口中的compareTo()方法完成的。但是由于HashSet子类没有实现Comparable接口,所以它判断重复元素的方式依靠的是Object类中的两个方法:hashCode()方法和equals()方法。
在Java中进行对象的比较操作:通过一个对象的唯一编码找到该对象的信息,当编码匹配时再调用equals()方法进行内容的比较
(1)hash码比较:
public native int hashCode();
(2)对象比较:
public boolean equals(Object obj)
所以Set接口中的HashSet实现判断数据重复的方式是覆写Object类的hashCode()方法以及equals()方法,下面进行相关操作:
package www.bit.java.work; import java.util.HashSet; import java.util.Objects; import java.util.Set; import java.util.TreeSet; class Person{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Person(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { return "Person [name=" + name + ", age=" + age + "]"; } @Override public int hashCode() { return Objects.hash(name,age); } @Override public boolean equals(Object obj) { if(obj==this) { return true; } if(obj==null||obj.getClass()!=this.getClass()) { return false; } Person person=(Person)obj; return Objects.equals(this.name,person.name) &&Objects.equals(this.age,person.age); } } public class Test5 { public static void main(String[] args) { Set<Person> set=new HashSet<>(); set.add(new Person("张三",11)); set.add(new Person("张三",11)); set.add(new Person("李四",11)); set.add(new Person("张三",12)); System.out.println(set); } }
运行结果:
[Person [name=李四, age=11], Person [name=张三, age=12], Person [name=张三, age=11]]
4. 集合标准输出
在之前的所有操作中,我们均利用toString()方法或者利用List接口中的get()方法对集合中的元素进行输出,但这些都不是集合的标准输出,集合的标准输出共有四种方式:Iterator、ListIterator、Enumration、foreach。
4.1 迭代输出:Iterator
在JDK1.5之前,Collection接口中就定义了iterator()方法用于取得Iterator接口的实例化对象,而在JDK1.5以后,将iterator()方法提升为Iterator接口中的方法。对于使用Iterator实现集合标准输出的三个重要方法:
(1)iterator()方法:用于取得集合对应的Iterator对象
(2)hasNext()方法:判断是否有下一个元素
(3)next()方法:取得当前元素
下面通过调用以上三个方法实现标准输出:
package www.bit.java.work; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class Test5 { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("ABC"); list.add("def"); list.add("hello"); list.add("HI"); //1.实例化Iterator对象 Iterator<String> iterator=list.iterator(); //2.判断是否有下一个元素 while(iterator.hasNext()) { //3.取得当前元素 String string=iterator.next(); System.out.println(string); } } }
运行结果:
ABC def hello HI
4.2 双向迭代输出:ListIterator
Iterator标准输出有一个特点是只能从前向后进行内容的迭代输出,如果想要进行双向迭代输出(即可以从前向后迭代输出也可以从后向前迭代输出),需要使用Iterator接口的子接口ListIterator接口。这里需要注意的是:只有List接口才可以使用ListIterator接口进行集合的标准输出,而Set接口无法使用ListIterator接口进行集合的标准输出。
先来介绍ListIterator接口:
public interface ListIterator<E> extends Iterator<E>
Iterator接口对象是由Collection接口支持的,ListIterator接口对象是由List接口支持的。List接口提供以下方法实例化ListIterator对象:
ListIterator<E> listIterator();
在使用ListIterator接口对集合进行标准输出的方法主要有以下几个:
(1)判断是否有下一个元素:hasNext()方法
(2)取得下一个元素:next()方法
(3)判断是否有前一个元素:hasPrevious()方法
(4)取得前一个元素:previous()方法
下面调用以上方法实现集合的双向标准输出:
package www.bit.java.work; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.ListIterator; public class Test5 { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("ABC"); list.add("def"); list.add("hello"); list.add("HI"); //1.实例化ListIterator对象 ListIterator<String> iterator=list.listIterator(); System.out.println("从前向后输出:"); //2.判断是否有下一个元素 while(iterator.hasNext()) { //3.取得当前元素 String string=iterator.next(); System.out.print(string+" "); } System.out.println("\n从后向前输出:"); //2.判断是否有下一个元素 while(iterator.hasPrevious()) { //3.取得当前元素 String string=iterator.previous(); System.out.print(string+" "); } } }
运行结果:
从前向后输出: ABC def hello HI 从后向前输出: HI hello def ABC4.3 枚举输出:Enumeration
在JDK1.0时引入了Enumeration输出接口,在JDK1.5时对Enumeration接口进行的一定的修改。先来观察Enumeration接口的定义:
public interface Enumeration<E>
在使用Enumeration接口实现集合的标准输出时,用到Enumeration接口的两个方法:
(1)判断是否有下一个元素:hasMoreElements()方法
(2)取得元素:nextElement()方法
但是要想取得Enumeration接口的对象,不再是依靠Collection、List、Set等接口,而是依靠Vector子类,因为早期设计Enumeration接口时就是为Vector子类服务的。在Vector子类中提供了一个取得Enumeration接口对象的方法:
public Enumeration<E> elements()
下面通过调用以上方法实现集合的标准枚举输出:
package www.bit.java.work; import java.util.Enumeration; import java.util.Vector; public class Test5 { public static void main(String[] args) { Vector<String> vector=new Vector<>(); vector.add("abc"); vector.add("hello"); vector.add("hi"); //1.实例化Enumeration接口对象 Enumeration<String> enumeration=vector.elements(); //2.判断是否有下一个元素 while(enumeration.hasMoreElements()) { String string=enumeration.nextElement(); System.out.println(string); } } }
运行结果:
abc hello hi
4.4 foreach输出
从JDK1.5开始foreach支持输出数组,实际上也可以利用它输出集合,调用如下:
package www.bit.java.work; import java.util.ArrayList; import java.util.List; public class Test5 { public static void main(String[] args) { List<String> list=new ArrayList<>(); list.add("hello"); list.add("world"); list.add("hello"); list.add("world"); for(String string : list) { System.out.println(string); } } }
运行结果:
hello world hello world
关于集合的4种输出方式:推荐使用Iterator。