一、集合
一个集合是代表一组对象的一个对象,集合中的这一组对象称为它的元素。集合主要来处理各种类型的对象的聚集。集合保留了对 Object 类的引用,因此,任何数据类型的对象都可以存放在集合中。
集合中包含3个重要接口: Collection、Set、List,它们都可以用来组织多个对象,但是又各不相同。
- Collection 集中的对象存放没有顺序,而且允许重复。
- Set 对象的无序聚集,但不允许重复。
- List 有序的对象聚集,对象按照一定顺序存放,同时允许重复。
集合的继承关系如图:
二、Collection 接口
Collection 接口是单值存放的最大父接口,可以向其中保存多个单值(单个对象)数据。此接口定义如下方法:
boolean add(E e) // 向集合中插入元素
boolean addAll(Collection<? extends E> c) // 将一个集合的内容插入进来
void clear() // 清除此集合中所有的元素
boolean contains(Object o) // 判断某一个对象是否在集合中存在
boolean containsAll(Collection<?> c) // 判断一组对象是否在集合中存在
boolean equals(Object o) // 对象比较
int hashCode() // 哈希码
boolean isEmpty() // 集合是否为空
Iterator<E> iterator() // 为 Iterator 接口实例化
boolean remove(Object o) // 删除指定元素
boolean removeAll(Collection<?> c) // 删除一组对象
boolean retainAll(Collection<?> c) // 保存指定内容
int size() // 求出集合的大小
Object[] toArray() // 讲一个集合变成对象数据
<T> T[] toArray(T[] a) // 指定好返回的对象数组类型
2.1 Collection 子接口
如果直接操作使用 Collection 接口,则表示操作意义不明确,所以在 Java 开发中不提倡使用直接操作 Collection 接口,主要子接口如下:
- List 可以存放重复的内容。
- Set 不能存放重复的内容,重复的内容靠 hashCode() 和 equals()两个方法区分。
- Queue 队列接口。
- SortedSet 可以对集合中的数据进行操作。
三、List 接口
与 Collection 接口不同的是,在 List 接口中大量地扩充了 Collection 接口,下面列出 List 中对象Collection 接口的扩展方法:
void add(int index, E element) // 在指定位置增加元素
boolean addAll(int index, Collection<? extends E> c) // 在指定位置增加一组元素
E get(int index) // 返回列表中指定位置的元素。
int indexOf(Object o) // 返回此列表中第一次出现的指定元素的索引
int lastIndexOf(Object o) // 返回此列表中最后出现的指定元素的索引
ListIterator<E> listIterator() // 为 ListIterator 接口实例化
E remove(int index) // 移除列表中指定位置的元素
List<E> subList(int fromIndex, int toIndex) // 取出集合的子集合
E set(int index, E element) // 用指定元素替换列表中指定位置的元素
void add(int index, E element) // 在指定位置增加元素
boolean addAll(int index, Collection<? extends E> c) // 在指定位置增加一组元素
E get(int index) // 返回列表中指定位置的元素。
int indexOf(Object o) // 返回此列表中第一次出现的指定元素的索引
int lastIndexOf(Object o) // 返回此列表中最后出现的指定元素的索引
ListIterator<E> listIterator() // 为 ListIterator 接口实例化
E remove(int index) // 移除列表中指定位置的元素
List<E> subList(int fromIndex, int toIndex) // 取出集合的子集合
E set(int index, E element) // 用指定元素替换列表中指定位置的元素
3.1 List 接口的常用子类
3.1.1 ArrayList
ArrayList 是 List 子类,可以直接通过对象的多态性为 List 接口实例化。
public class ArrayListDemo01 {
public static void main(String[] args) {
List<String> allList = null; // 定义 List 对象
Collection<String> allCollection = null; // 定义 Collection 对象
allList = new ArrayList<String>();
allCollection = new ArrayList<String>();
// 向集合中添加元素
allList.add("hello");
allList.add(0, "world"); // List 扩展 Collection 的 add()方法
System.out.println(allList);
allCollection.add("HELLO");
allCollection.add("WORLD");
allList.addAll(allCollection);
System.out.println(allList);
// 删除元素
allList.remove(0); // List 扩展 Collection 的remove() 方法
allList.remove("HELLO");
System.out.println(allList);
// 遍历元素
for (int i = 0; i < allList.size(); i++) {
System.out.println(allList.get(i));
}
// 将集合变成对象数组
String[] strings = allList.toArray(new String[]{}); // allList.toArray()
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
}
}
3.1.2 Vector
从整个 Java 的集合发展历史来看,Vector算是一个元老级的类,在JDK1.0时就已经存在此类。到了JDK1.2之后重点强调集合框架的概念,所以定义很多新接口(如List等),但是考虑一大部分用户已经习惯了使用 Vector 类,所以 Java 的设计者就让 Vector 类多实现了一个 List 接口,这才将其保留下来。由于其是 List 子类,所以 Vector 类的使用与之前的并没有太大的区别。
public class VectorDemo01 {
public static void main(String[] args) {
List<String> allList = null;
allList = new Vector<String>();
// 直接使用的是 List 接口進行操作
allList.add("hello");
allList.add(0, "world");
for (int i = 0; i < allList.size(); i++) {
System.out.print(allList.get(i) + " ");
}
System.out.println();
System.out.println("---------------------");
// 使用 Vector 類中的 addElement(E o) 方法,是最早的向集合中增加元素的操作
Vector<String> vectors = new Vector<String>();
vectors.addElement("pegasus");
vectors.addElement("flyingdancing");
Enumeration<String> enums = vectors.elements();
while (enums.hasMoreElements()) {
String value = enums.nextElement();
System.out.println(value);
}
}
}
3.1.3 ArrayList 与 Vector 的区别
ArrayList 在 JDK1.2 之后推出的,属于新的操作类。采用异步处理方式,性能更高,属于非线程安全的操作类,只能使用 Iterator、foreach输出。
Vector 在 JDK1.0 时推出,属于旧的操作类。采用同步处理方式,性能较低,属于线程安全的操作类,可以使用 Iterator、foreach、Enumeration 输出。
3.1.4 LinkedList 子类与 Queue 接口
LinkedList 表示的是一个链表的操作类,此类实现了 List 接口,同时实现了 Queue 接口。Queue 表示的是队列操作接口,采用 FIFO(先进先出)的方式操作。
Queue 接口定义的方法
public E element() // 找到链表的标表头
public boolean offer(E o) // 将指定元素增加到链表的结尾
public E peek() // 找到但并不删除链表的头
public E poll() // 找到并删除此链表的头
public E remove() // 检索并移除表头
LinkedList 类的主要方法
public void addFirst(E o) // 在链表开头增加元素
public void addLast(E o) // 在链表结尾增加元素
public boolean offer(E o) // 将指定元素增加到链表的结尾
public E removeFirst() // 删除链表的第一个元素
public E removeLast() // 删除链表的最后一个元素
示例操作
public class LinkedListDemo01 {
public static void main(String[] args) {
LinkedList<String> linkedLists = new LinkedList<String>();
linkedLists.add("A");
linkedLists.add("B");
linkedLists.add("C");
System.out.println("初始化鏈表:" + linkedLists);
linkedLists.addFirst("X");
linkedLists.addLast("Y");
System.out.println("增加頭和尾之後的鏈表:" + linkedLists);
System.out.println("1-1、element() 方法找到表頭:" + linkedLists.element());
System.out.println("2-1、peek()方法找到表頭:" + linkedLists.peek());
System.out.println("2-2、找到之後的鏈表內容:" + linkedLists);
System.out.println("3-1、poll()方法找到表頭:" + linkedLists.poll());
System.out.println("3-2、找到之後的鏈表內容:" + linkedLists);
System.out.println("以FIFO的方式輸出:");
for (int i = 0; i < linkedLists.size(); i++) {
System.out.print(linkedLists.get(i) + "、");
}
}
}
四、Set
Set 接口也是 Collection 接口的子接口,但与Collection 或 List 接口不同的是,Set 接口中不能加入重复的元素。Set 接口的实例无法像 List 接口那样可以进行双向输出,因为此接口没有提供像 List 接口的 get(int index) 方法。
4. 1 Set 接口的常用子类
4.1.1 HashSet
HashSet 线程不安全,存取速度快。可以通过元素的两个方法, hashCode 和 equals 来完成保证元素唯一性。如果元素的 hashCode 值相同,才会判断 equals 是否为 true 。如果元素的 hashCode 值不同,不会调用 equals。
示例:
public class HashSetDemo01 {
public static void main(String[] args) {
HashSet<Person> hs = new HashSet<Person>();
hs.add(new Person("pegasus", 21));
hs.add(new Person("mbyd", 16));
hs.add(new Person("flyingdancing", 35));
hs.add(new Person("wbjbaqblca", 10));
hs.add(new Person("wbjbaqblca", 10));
Iterator<Person> iterators = hs.iterator();
while (iterators.hasNext()) {
Person person = iterators.next();
System.out.println(person.getName() + ", " + person.getAge());
}
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
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;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
}
4.1.2 TreeSet
特点
a)、底层的数据结构为二叉树结构(红黑树结构)
b)、可对Set集合中的元素进行排序,是因为:TreeSet类实现了Comparable接口,该接口强制让增加到集合中的对象进行了比较,需要复写compareTo方法,才能让对象按指定需求进行排序,并加入集合。
注意:排序时,当主要条件相同时,按次要条件排序。
c)、保证数据的唯一性的依据:通过compareTo方法的返回值,是正整数、负整数或零,则两个对象较大、较小或相同。相等时则不会存入。
Tree排序的两种方式
a)、第一种排序方式:自然排序
让元素自身具备比较性。元素需要实现Comparable接口,覆盖compareTo方法。这种方式也被称为元素的自然顺序,或者叫做默认顺序。
b)、第二种方式:比较器
当元素自身不具备比较性时,或者具备的比较性不是所需要的。这时就需要让集合自身具备比较性。
在集合初始化时,就有了比较方式。定义一个比较器,将比较器对象作为参数传递给TreeSet集合的构造函数。
比较器构造方式:定义一个类,实现Comparator接口,覆盖compare方法。
当两种排序都存在时,以比较器为主。
示例:
public class TreeSetDemo01 {
public static void main(String[] args) {
TreeSet<Person> hs = new TreeSet<Person>(new LenCompare());
hs.add(new Person("pegasus", 21));
hs.add(new Person("mbyd", 16));
hs.add(new Person("flyingdancing", 35));
hs.add(new Person("wbjbaqblca", 10));
hs.add(new Person("wbjbaqblca", 10));
Iterator<Person> iterators = hs.iterator();
while (iterators.hasNext()) {
Person person = iterators.next();
System.out.println(person.getName() + ", " + person.getAge());
}
}
}
class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
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;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public int compareTo(Person o) {
Person person = o;
if (this.age == person.age) {
return this.name.compareTo(person.name);
}
return this.age - person.age;
}
}
// 定义一个比较器,以姓名长度为主要比较
class LenCompare implements Comparator<Person> {
public int compare(Person o1, Person o2) {
int num = new Integer(o1.getName().length()).compareTo(new Integer(o2.getName().length()));
if (num == 0) {
return new Integer(o1.getAge()).compareTo(o2.getAge());
}
return num;
}
}
4.1.3 HashSet 与 TreeSet 区别
1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值 。
2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束 。
3、HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例。