为什么出现集合类?
面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就要对对象进行存储,集合就是存储对象最常用的一种方式。
数组和集合类同是容器,有何不同?
数组虽然也可以存储对象,但长度是固定的;集合长度是可变的。数组中可以存储任意数据类型,集合只能存储对象。
集合类的特点:
集合只用于存储对象,集合长度是可变的,集合可以存储不同类型的对象。
Java集合类是一种特别有用的工具类,可用于存储数量不等的对象,并可以实现常用的数据结构,如栈、队列等。除此之外,Java集合还可用于保存具有映射关系的关联数组。Java集合大致可分为List、Set、Queue和Map四种体系,其中List代表有序、重复的集合;Set代表无序、不可重复的集合;而Map则代表具有映射关系的集合,Java5又增加了Queue体系集合,代表一种队列集合实现。
为了保存数量不确定的数据,以及保存具有映射关系的数据(也被称为关联数组), Java提供了集合类。集合类主要负责保存、盛装其他数据,因此集合类也被称为容器类,所有的集合类都位于 java.util包下。 集合类和数组不一样,数组元素既可以是基本类型的值,也可以是对象(实际上保存 的是对象的引用变量);而集合里只能保存对象(实际上只是保存对象的引用变量,但通常习惯上认为集合里保存的是对象)。 Java的集合类主要由两个接口派生而出: Collection和Map, Collection和Map是Java 集合框架的根接口,这两个接口又包含了一些子接口或实现类。
如下所示是 Java集合 简单结构图:
两大接口:Java 集合类主要由两个接口派生出来:
1、Collection
List:可存放重复对象,有序;Set:不能存放重复对象;Queue:队列;SortedSet:可对集合数据排序;
2、Map
SortedMap :可对集合数据排序
(1)List集合/接口(元素可以重复)
List是 Collection 子接口,是有序的集合,集合中每个元素都有对应的顺序序列。List 集合可使用重复元素,可以通过索引来访问指定位置的集合元素(顺序索引从 0 开始),List 集合默认按元素的添加顺序设置元素的索引,比如第一个元素的索引就是 0,好似数组。
示例代码(以ArrayList为例):
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class ListDemo {
public static void main(String[] args) {
List<Integer> l = new ArrayList<Integer>();
l.add(12);
l.add(24);
l.add(36);
l.add(23);
l.add(37);
System.out.println(l);
ListIterator<Integer> it = l.listIterator();
while (it.hasNext()) {
System.out.println("正序:" + it.next());
}
System.out.println("==================================");
// 使用向前迭代前必须将游标(指针)移动到后边!
while (it.hasPrevious()) {
System.out.println("逆序:" + it.previous());
}
}
}
结果截图:
List 接口中常用类:
Vector:线程安全,但速度慢,已被 ArrayList 替代。 ArrayList:线程不安全,查询速度快。 LinkedList:链表结构,增删速度快。取出 List 集合中元素的方式: get(int index):通过脚标获取元素。 iterator():通过迭代方法获取迭代器对象。 ArrayList 和 Vector 类都是基于数组实现的 List 类,Vector 比较古老,被 ArrayList 取代了;ArrayList 是线程不安全的,而 Vector 是线程安全的,但是即使这样,也不推荐使用 Vector。因为 Collections 有方法可以得到线程安全的 ArrayList 对象; Collections 类::static List synchronizedList(List list) 返回指定列表支持的同步(线程安全的)列表。
(2)Set集合/接口(元素不可以重复)
Set 是 Collection 子接口; Set 和 Collection 基本上一样,一点除外:Set 无法记住添加的顺序,不允许包含重复的元素。 当试图添加两个相同元素进 Set 集合,添加操作失败,add()方法返回 false。 Set 判断两个对象是否相等用 equals,而不是使用==。 也就是说两个对象 equals 比较返回 true,Set 集合是不会接受这个两个对象的。常用子类:HashSet:散列存放;TreeSet:有序存放。
hashCode 方法对于 HashSet 的作用:
HashSet 类是 Set 接口最常用的实现类,采用 hash 算法存储数据,具有良好的存储和查找功能。散列存储:不记录添加顺序;排列顺序时,顺序有可能发生变化;线程不安全的,多个线程访问一个 HashSet 要使用同步代码;HashSet 集合元素值允许是 null,但是最多只能有一个;因为 Set 集合就不可以装重复的对象! hash(翻译为哈希,或散列)算法的功能:保证通过一个对象快速找到另一个对象;其算法价值体现在速度,可以保证查询快速执行;当从 HashSet 中访问元素时,HashSet 先计算该元素的 hashCode(也就是该对象的 hashCode 方法返回值),然后直接到该 HashCode 对应的位置取出该元素;在这里对象的 hashCode 就好比是数组里的索引,但是不是索引;
HashSet 元素添加
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode()方法来得到该对象的 hashCode 值,判断已经存储在集合中的对象的 hashCode 值是否与添加的对象的 hashCode 值一致;若不一致,直接添加进去;若一致,再进行 equals 方法比较,equals 方法如果返回 true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去;如果我们重写了 equals 方法,也要重写 hashCode 方法,反之亦然。 HashSet 集合判断两个元素相等的标准是两个对象通过 equals 方法比较相等,并且两个对象的 hashCode 方法返回值也相等。如果需要某个类的对象保存到 HashSet 集合中,覆写该类的 equals()和 hashCode()方法,应该尽量保证两个对象通过 equals 比较返回 true 时,他们的 hashCode 返回也相等。
示例代码1:
import java.util.HashSet;
import java.util.Iterator;
public class SetDemo {
public static void main(String[] args) {
HashSet<String> set = new HashSet<>();
System.out.println("==========添加元素===========");
set.add("A");
set.add("B");
set.add("C");
set.add("A");
set.add(null);// 可以放入null,但只能放入一个null
System.out.println(set.toString());
System.out.println("==========移除元素===========");
set.remove("A");
System.out.println(set.toString());
System.out.println("==========迭代器遍历===========");
Iterator<String> iterator = set.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + ",");
}
System.out.println();
System.out.println("==========获取元素个数===========");
System.out.println(set.size());
System.out.println("==========是否包含某个元素===========");
System.out.println(set.contains("B"));
System.out.println("==========清空元素===========");
set.clear();
System.out.println(set.isEmpty());
}
}
结果截图:
示例代码2:
public class PersonDemo {
private String name;
public PersonDemo(String name) {
super();
this.name = name;
}
@Override
public String toString() {
return "name=" + name;
}
// 没有重写hashcode和equals方法前,显示三次(一样的)。重写后,只剩下一个了!说明重写后方法起作用了,重复的输入不进去!
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
PersonDemo other = (PersonDemo) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
没有重写hashcode和equals方法前结果截图:显示三次(一样的)。
重写后结果截图,只剩下一个了!说明重写后方法起作用了,重复的输入不进去!
(3)Queue队列/接口
Queue:基本上,一个队列就是一个先入先出(FIFO)的数据结构;Queue接口与List、Set同一级别,都是继承了Collection接口。LinkedList实现了 Deque接口
示例代码:
import java.util.LinkedList;
import java.util.Queue;
public class QueueDemo {
public static void main(String[] args) {
Queue<String> queue = new LinkedList<String>();
// 添加元素// add()和remove()方法在失败的时候会抛出异常(不推荐使用)
queue.offer("同学A");
queue.offer("同学B");
queue.offer("同学C");
System.out.println("======遍历队列======");
for (String q : queue) {
System.out.print(q + ",");
}
System.out.println();
System.out.println("======poll返回第一个元素,并在队列中删除======");
System.out.println("poll=" + queue.poll());
System.out.println(queue);
System.out.println("======element返回第一个元素======");
System.out.println("element=" + queue.element());
System.out.println(queue);
System.out.println("======peek返回第一个元素======");
System.out.println("peek=" + queue.peek());
System.out.println(queue);
}
}
结果截图: