一、集合框架图
Java 集合框架主要包括两种类型的容器:一种是集合(Collection),存储一个元素集合;另一种是图(Map),存储键/值对映射。Collection 接口又有 3 种子类型:List、Set 和 Queue。再下面是一些抽象类,最后是具体实现类,常用的有 ArrayList、LinkedList、HashSet、LinkedHashSet、HashMap、LinkedHashMap 等等。
集合框架内容:
集合框架是一个用来代表和操纵集合的统一架构。所有的集合框架都包含如下内容:
1、接口
代表集合的抽象数据类型。例如 Collection、List、Set、Map 等。之所以定义多个接口,是为了以不同的方式操作集合对象
2、实现(类)
集合接口的具体实现。从本质上讲,它们是可重复使用的数据结构,例如:ArrayList、LinkedList、HashSet、HashMap。
3、算法
实现集合接口的对象里的方法执行的一些有用的计算,例如:搜索和排序。这些算法被称为多态,那是因为相同的方法可以在相似的接口上有着不同的实现。
除了集合,该框架也定义了几个 Map 接口和类。Map 里存储的是键/值对。尽管 Map 不是集合,但是它们完全整合在集合中。
二、集合框架体系
集合框架体系如图所示:
Java 集合框架提供了一套性能优良,使用方便的接口和类,java集合框架位于java.util包中, 所以当使用集合框架的时候需要进行导包。
1、集合接口
集合框架定义了一些接口。
Set 和 List 的区别
(1)
Set 接口实例存储的是无序的,不重复的数据
。List 接口实例存储的是有序的,可以重复的元素
。
(2)Set 检索效率低下,删除和插入效率高,插入和删除不会引起元素位置改变
(实现类有 HashSet、TreeSet)。
(3)List 和数组类似,可以动态增长,根据实际存储的数据的长度自动增长 List 的长度。查找元素效率高,插入删除效率低,因为会引起其他元素位置改变
(实现类有ArrayList、LinkedList,Vector) 。
2、集合实现类(集合类)
Java 提供了一套实现了 Collection 接口的标准集合类。其中一些是具体类,这些类可以直接拿来使用,而另外一些是抽象类,提供了接口的部分实现。
java.util 包中定义的类:
3、集合算法
集合框架定义了几种算法,可用于集合和映射。这些算法被定义为集合类的静态方法。
4、迭代器 Iterator
迭代器是一种模式、详细可见其设计模式,可以
使得序列类型的数据结构的遍历行为与被遍历的对象分离,即我们无需关心该序列的底层结构是什么样子的。只要拿到这个对象,使用迭代器就可以遍历这个对象的内部
。
ListIterator 继承了 Iterator,以允许双向遍历列表和修改元素
Iterable:实现这个接口的集合对象支持迭代,是可以迭代的,实现了这个可以配合 foreach 使用。List、Set 和 Map 的实现类均实现了 Iterable 接口,所以其实现类均可以迭代。
Iterator:迭代器,提供迭代机制的对象,具体如何迭代是这个 Iterator 接口规范的。
(1)Iterator 源码
public interface Iterator<E> {
// 检测集合中是否还有元素
boolean hasNext();
// 返回迭代器的下一个元素,并且更新迭代器的状态
E next();
// 将迭代器返回的元素删除
/*删除最近一次已近迭代出出去的那个元素。
只有当next执行完后,才能调用remove函数。
比如你要删除第一个元素,不能直接调用 remove() 而要先next一下( );
在没有先调用next 就调用remove方法是会抛出异常的。
这个和MySQL中的ResultSet很类似
*/
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
(2)Iterator 对象的获得
List、Set 和 Map 的实现类都提供了 iterator() 方法来获得自身容器的迭代器。
例如:
List<String> list=new ArrayList<String>();
Iterator<String> listiter=list.iterator();
Set<String> set=new HashSet<String>();
Iterator<String> setiter=set.iterator();
Map<String, String> map=new HashMap<String, String>();
// 获得 Entry 的迭代器
Iterator<Map.Entry<String, String>> mapiter=map.entrySet().iterator();
(3)ListIterator 双向迭代
public interface ListIterator<E> extends Iterator<E> {
// 判断是否还有下一个元素,有则返回true (正向遍历)
boolean hasNext();
// 返回列表中的下一个元素,并且前进指针位置
E next();
// 判断是否还有上一个元素,有则返回true (反向遍历)
boolean hasPrevious();
// 返回列表中的上一个元素,并向后(上)移动指针位置
E previous();
int nextIndex();
int previousIndex();
void remove();
// 用指定的元素替换由 next()或 previous()返回的最后一个元素
void set(E e);
// 将指定的元素插入列表的末尾
void add(E e);
}
5、集合遍历
一般遍历数组都是采用 for 循环或者增强 for,这两个方法也可以用在集合框架,但是还有一种方法是采用迭代器遍历集合框架,它是一个对象,实现了 Iterator 接口或 ListIterator 接口。
(1)遍历 ArrayList
import java.util.*;
public class Test{
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("Hello");
list.add("World");
list.add("HAHAHAHA");
//第一种遍历方法使用 For-Each 遍历 List
for (String str : list) {
System.out.println(str);
}
//第二种遍历,把链表变为数组相关的内容进行遍历
String[] strArray=new String[list.size()];
list.toArray(strArray);
for(int i=0;i<strArray.length;i++) {
System.out.println(strArray[i]);
}
//第三种遍历 使用迭代器进行相关遍历
Iterator<String> ite=list.iterator();
while(ite.hasNext()) {
System.out.println(ite.next());
}
}
}
三种方法都是用来遍历 ArrayList 集合,第三种方法是
采用迭代器的方法,该方法可以不用担心在遍历的过程中会超出集合的长度
。
(2)遍历 Set
// 1、迭代遍历:
Set<String> set = new HashSet<String>();
Iterator<String> it = set.iterator();
while (it.hasNext()) {
String str = it.next();
System.out.println(str);
}
// 2.for循环遍历:
for (String str : set) {
System.out.println(str);
}
// 3、set.forEach(java8 才支持)
Set<String> set = new HashSet<String>();
set.forEach(item -> {
System.out.println(item);
});
// 4、set.stream().forEach (java8 才支持)
Set<String> set = new HashSet<String>();
set.stream().forEach(item -> {
System.out.println(item);
});
(3)遍历 Map
import java.util.*;
public class Test{
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("1", "value1");
map.put("2", "value2");
map.put("3", "value3");
//第一种:普遍使用,二次取值
System.out.println("通过 Map.keySet 遍历 key和value:");
for (String key : map.keySet()) {
System.out.println("key= "+ key + " and value= " + map.get(key));
}
//第二种:迭代器
System.out.println("通过 Map.entrySet 使用 iterator 遍历 key 和 value:");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第三种:推荐,尤其是容量大时
System.out.println("通过 Map.entrySet 遍历 key 和 value");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= " + entry.getValue());
}
//第四种
System.out.println("通过 Map.values() 遍历所有的 value,但不能遍历 key");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
遍历方式的比较:
(1)
for 循环适合访问顺序结构,可以根据下标快速获取指定元素
。而Iterator 适合访问链式结构,因为迭代器是通过 next() 和 Pre() 来定位的
,可以访问没有顺序的集合。
(2)使用 Iterator 的好处在于可以使用相同方式去遍历集合中元素,而不用考虑集合类的内部实现(只要它实现了 java.lang.Iterable 接口)
。如果使用 Iterator 来遍历集合中元素,一旦不再使用 List 转而使用 Set 来组织数据,那遍历元素的代码不用做任何修改,如果使用 for 来遍历,那所有遍历此集合的算法都得做相应调整,因为List有序,Set无序,结构不同,他们的访问算法也不一样(遍历和集合本身分离)。
5、比较器
使一个类具备比较的能力只需要其实现具有比较能力的接口即可
。
(1)实现 Comparable 接口
Comparable 接口:
这个接口要在自定义的类上实现
,比如定义了一个 Person 类,实现 Comparable 接口,除了该类应有的属性、构造器、方法外,我们需要实现该接口中的抽象方法 compareTo() 并且需要指定其泛型
(也就是要比较的引用数据的类型
),意思就是说要比较的是泛型也就是<>中间的类型
。通过重写 compareTo() 方法来重写自己需要的比较规则,程序执行时底层会自动调用该方法
。
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
class Stu implements Comparable<Stu>{
private int id;
private String name;
private int age;
public Stu() {
super();
}
public Stu(int id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
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;
}
@Override
public String toString() {
return "Stu [id=" + id + ", name=" + name + ", age=" + age + "]";
}
//重写接口的方法
@Override
public int compareTo(Stu o) {
// 如果年龄相同就按名字排序
if(o.age==this.age){
return this.name.compareTo(o.name); // 注意:这里调用的 compareTo 方法是 String 类中重写的 compareTo 方法,并不是我们重写的方法。
}else{
// 年龄不同,按年龄排序
return this.age-o.age;
}
// 拿新的对象与几何中的已有对象分别比较
return this.age-o.age; // 按年龄升序
// 进来一个新的元素放到集合中,分别从左到右比较集合中所有元素,当返回值 >=0 时,新的元素位置不变,当返回值 <0 时,位置交换,然后依次比较所有元素
}
}
//建立一个集合 Stu 类型的 有属性 id,name, age
//将集合中的元素按照 age 进行排序
public class TestComparable{
public static void main(String[] args) {
List<Stu> list = new ArrayList<Stu> ();
list.add(new Stu(1,"lhj",25));
list.add(new Stu(2,"zj",23));
list.add(new Stu(3,"hj",15));
Collections.sort(list);
System.out.println(list);
}
}
(2)实现 Comparator 接口
Comparator 接口:这个接口需要我们
自定义一个类来实现它,我们可以称这个类为比较器
,并且需要指定比较类型类型(用泛型来制指定,在实现接口的时候指定)
,我们需要实现该接口中的抽象方法 compare()
,只要实现了该比较方法就已经实现了比较器的功能,当然也可以加别的属性和方法。
在创建集合对象的时候将比较器传入,从而使集合对象具有比较功能。
//比较器二:实现 Comparator 接口,在 compare() 方法中编写比较规则
class MyComparator1 implements Comparator<Person>{
public int compare(Person p1,Person p2){
// 年龄相同用名字排序
if(p1.age==p2.age){
return p2.name.compareTo(p1.name);
}else{
// 年龄不同用年龄排序
return p1.age-p2.age;
}
}
}
// 要传入写好的比较器
Set<Person> set=new TreeSet<>(new MyComparator1()); //传入比较器
上述比较器的优缺点:
(1)实现 Comparable 接口的比较器,因为
要在要比较的类上实现,所以比较规则只能重写一次,比较规则比较单一,适用于比较规则不变的时候使用此方法
。
(2)实现 Comparator 接口的比较器,因为是新建一个类实现 Comparator 接口,而我们可以新建很多类来实现 Comparator 接口,所以可以写很多种不同的比较规则,适用于比较规则多变的情况,根据需要传入相应的比较器即可,比较灵活多变
。