Collection体系
根据jdk介绍它是整个集合框架的根节点
实现了它的接口可以存放一组任意多个、任意类型的数据。众所周知的List和Set都可以看作是Collection,它们有的允许元素重复(List),有的不允许(Set)
下面就用一张图来看看它们之间的具体关系:
Collection体系图
List
List,有序集合(也称为序列)。实现了Collection,所以可以说它就是一个Collection,它具有了Collection的所有非私有特性,并且在此基础之上拓展出了一些属于自己的
特有属性及功能。这个接口的用户对列表中的每个元素都有精确的控制。用户可以通过其整数索引(列表中的位置)访问元素,并在列表中搜索元素
ArrayList
底层实现是基于数组的,实现所有可选列表操作,并允许所有元素,包括null。除了实现列表接口之外,这个类还提供了方法来操作在内部使用的数组的大小来存储列表。(这个类大
致相当于向量,但它是不同步的。)可以通过List list = Collections.synchronizedList(new ArrayList(...));获得同步的ArrayList集合。ArrayList是可以自动扩容的,
但是它的实现原理也无非就是重新创建一个容器再将现有元素赋值到新容器中,再把变量指向修改。
用一些代码来看看它具体提供的一些方法:
import java.util.ArrayList;
import java.util.Collection;
/**
* ArrayList
*
* @author xer
*
*/
public class ArrayListTest {
public static void main(String[] args) {
ArrayList list = new ArrayList();
list.add("XX");
list.add(true);
list.add("XX");
list.add("OO");
list.add(100);
list.add("XX");
list.add("xx");
list.add("tt");
list.add("ss");
System.out.println( list.size());
/**
* add(int index, E element)
* Inserts the specified element at the specified position in this list.
* 在指定位置插入元素
*/
list.add(2,"next");
System.out.println(list.size());
System.out.println(list);
/**
* addAll(Collection<? extends E> c)
* Appends all of the elements in the specified collection to the end of this list,
* in the order that they are returned by the specified collection's Iterator.
* 在列表尾部插入一个集合
*/
Collection col = new ArrayList();
col.add("xx");
list.addAll(col);
System.out.println(list.size());
System.out.println(list);
/**
* contains(Object o)
* Returns true if this list contains the specified element.
* 如果列表中有此元素则返回true
*/
System.out.println(list.contains("xx"));
/**
* get(int index)
* Returns the element at the specified position in this list.
* 返回指定下标处的元素
*/
System.out.println(list.get(3));
/**
* indexOf(Object o)
* Returns the index of the first occurrence of the specified element in this list,
* or -1 if this list does not contain the element.
* 返回指定元素在列表中第一次出现的位置,如果为找到则返回-1
*/
System.out.println(list.indexOf("XX"));
/**
* remove(int index)
* Removes the element at the specified position in this list.
* 删除指定下标处元素
*/
list.remove(2);
System.out.println(list.size());
System.out.println(list);
/**
* set(int index, E element)
* Replaces the element at the specified position in this list with the specified element.
* 替换指定下标处的元素为传入的指定元素
*/
list.set(2, "瘦狗");
System.out.println(list.size());
System.out.println(list);
}
}
在ArrayList中遍历元素的方法
- for循环遍历
- foreach遍历
- Iterator迭代器遍历
- ListIterator双向遍历(提供正向遍历与反向遍历)
for遍历
System.out.print("for:");
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i)+", ");
}
foreach遍历(其实它的底层实现就是迭代器)
for (Object object : list) {
System.out.print(object+", ");
}
Iterator迭代器遍历
/**
*boolean hasNext():判断是否还有下一个元素
*E next():返回这个迭代器中的下一个元素
*/
Iterator iterator = list.iterator();
while(iterator.hasNext()) {
System.out.print(iterator.next()+", ");
}
ListIterator双向遍历
/**
*boolean hasPrevious():返回true,如果上一个元素不为空
*E previous():返回这个迭代器中的上一个元素
*/
System.out.print("listIterator-next:");
ListIterator listIterator = list.listIterator();
while (listIterator.hasNext()) {
System.out.print(listIterator.next()+", ");
}
System.out.println();
System.out.print("listIterator-pervious:");
while(listIterator.hasPrevious()) {
System.out.print(listIterator.previous()+", ");
}
LinkedList
底层实现是基于链表的,实现所有可选列表操作,并允许所有元素(包括null)。由于它也实现了 Deque<E>, Queue<E> 接口,所以也可以充当队列以及堆栈结构使用。
LinkedList也是支持索引的,不过相较于ArrayList,LinkedList的优势在新增、删除方面更大一些。同样LinkedList也是线程不安全的,可以通过
List list = Collections.synchronizedList(new LinkedList(...));获得一个同步的LinkedList集合
LinkedList的基本方法与遍历操作等于ArrayList大同小异,这里就不再过多赘述
Set
Set也是可以存放任意多个、任意类型的数据,但是是无序的(存放顺序与迭代顺序)并且不允许重复
HashSet
底层实现基于数组,不允许重复,这个类实现了集合接口,由哈希表(实际上是HashMap实例)支持。它对集合的迭代顺序没有任何保证;这个类允许空元素
如何判断元素重复的
- equals
hashCode
实际开发中我们通常放入HashSet中的元素是属于自定义类型的,但是我们的类型并不能被正确的判断是否重复,因为java定义好的类型都是重写了equals和hashCode方法的。所以如果我们想要正确存入自定义类型就必须重写这两个方法
import java.util.HashSet; import java.util.Random; public class HashSetTest { public static void main(String[] args) { HashSet hashSet = new HashSet(); hashSet.add(10); hashSet.add(10); hashSet.add(10); hashSet.add("10"); hashSet.add("10"); Student stu = new Student("tom", 22); Student stu1 = new Student("tom", 22); hashSet.add(stu1); hashSet.add(stu); System.out.println(hashSet.size()); System.out.println(hashSet); //在HashSet中不同元素类型也会被看成不同的元素 } } class Student { String name; int age; public Student(String name, int age) { super(); this.name = name; this.age = age; } @Override public String toString() { // TODO Auto-generated method stub return "name:"+name+" age:"+age; } //IDE自动生成的hashCode以及equals方法 @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; Student other = (Student) 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; } }
TreeSet
TreeSet底层实现是基于红黑树的,也就是二叉树。它是无序的,不允许重复,在它的内部元素按照自然排序排列,如果你自定义的类没有实现Comparable接口,是直接会添加
失败的。当然,你也可以选择自定义一个定制排序器:自定义排序器类实现Comparator接口,覆写compare方法,再使用TreeSet的有参构造方法把排序器移植到TreeSet中成
为TreeSet内部的“裁判”,那么它也是可以实现自己排序的。有一点需要注意:理论上TreeSet是可以存放任意类型的数据,但是一旦它内部存放了一种类型数据后就不能再
存放其他类型的数据了。这一点想想就能明白了,TreeSet它存放数据是要对它们进行排序的,如果两个对象都不是一个类型的,那要怎么比较呢?
下面我们就一起用代码来看看具体实现,
首先,我们看看TreeSet自己内部的自然排序是怎样的
import java.util.TreeSet;
/**
* TreeSet中可以存放任意类型的元素,并且都是有序的(自然排序)
* 一旦放入了一种类型的元素之后就不能放入其他类型的元素
*
*
* 放入自定义类型
* 放入的自定义类型必须是实现了Comparable接口的
* @author xer
*
*/
public class TreeSetTest {
public static void main(String[] args) {
TreeSet treeSet = new TreeSet();
Student student2 = new Student("Tom",12);
Student student = new Student("Tom",11);
Student student1 = new Student("Tom",11);
treeSet.add(student2);
treeSet.add(student1);
treeSet.add(student);
System.out.println(treeSet.size());
System.out.println(treeSet);
}
}
class Student implements Comparable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//如果覆写compareTo方法,那么它默认返回0,说明两个元素是相等的(重复)
/**
* compareTo方法会返回一个整数,
* 负整数、0、整数
* 负整数说明此元素小于传入元素
* 0说明两元素相等
* 正整数说明此元素大于传入元素
*/
@Override
public int compareTo(Object o) {
Student stu = (Student)o;
if(this.age > stu.age) {
return 1;
}else if(this.age < stu.age) {
return -1;
}else {
return this.name.compareTo(stu.name);
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
结果:
明显可以看出两个元素是同一个元素,所以只会装入一个,但是不同的两个元素装入之后是有序的
现在,在现实生活中,有许多时候我们都需要用到其他人写好的类,可能他们本身就已经写好了排序的规则(覆写好了comparTo方法)但是我们想要不同的排序规则,这时我们又不能去修改他们的类,要怎么办呢?
这时我们就能用到定制排序了
定制排序:自定义一个定制排序器类实现Comparator接口并且覆写compare方法
下面是具体代码实现:
StudentComparator.java (自定义的排序器类)
import java.util.Comparator;
/**
* 自定义比较器
* @author xer
*
*/
public class StudentComparator implements Comparator{
/**
* 根据传入的两个元素大小比价,返回大于:整数,等于:0,小于:负数
*/
@Override
public int compare(Object o1, Object o2) {
Student stu1 = (Student)o1;
Student stu2 = (Student)o2;
if(stu1.age > stu2.age) {
return 1;
}else if(stu1.age < stu2.age) {
return -1;
}else {
return stu1.name.compareTo(stu2.name);
}
}
}
TreeSetTest.java
import java.util.TreeSet;
/**
*
*
* 自定义比较器
* 当自定义的类具有自己的自然排序能力时,再给TreeSet设置一个自定义比较器
* 会采用自定义比较器的比较规则
* @author xer
*
*/
public class TreeSetTest {
public static void main(String[] args) {
StudentComparator sc = new StudentComparator();
TreeSet treeSet = new TreeSet(sc); //将自定义的比较器传入,成为“裁判”
Student student = new Student("Tom",12);
Student student1 = new Student("Tom",11);
treeSet.add(student1);
treeSet.add(student);
System.out.println(treeSet.size());
System.out.println(treeSet);
}
}
class Student implements Comparable{
String name;
int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//如果覆写compareTo方法,那么它默认返回0,说明两个元素是相等的(重复)
/**
* compareTo方法会返回一个整数,
* 负整数、0、整数
* 负整数说明此元素小于传入元素
* 0说明两元素相等
* 正整数说明此元素大于传入元素
*/
@Override
public int compareTo(Object o) {
Student stu = (Student)o;
if(this.age > stu.age) {
return -1;
}else if(this.age < stu.age) {
return 1;
}else {
return this.name.compareTo(stu.name);
}
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
}
结果:
可以看到,无论Student类是否实现Comparable接口并且定义重写了compareTo方法,TreeSet内部都会按照我们的自定义排序器内的规则进行排序,这样就实现了既不用改别人的代码也可以实现自己的想法的愿望了。这也充分的体现了面向对象的思想。