目录
1.List 集合
2.Set 集合
1.List 集合
List 继承了 Collection 接口,是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素,可以精确的将元素插入指定位置。
(1) List 集合中常用的一些方法:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(2)ArrayList 集合
ArrayList 集合是 List 集合的实现类,基于顺序表结构实现了 List 集合,但它是线程不安全的。Vector 集合与之作用相同,但它是一个线程安全的集合。由于考虑到线程同步问题,相比于 ArrayList 集合效率要低了很多。因此通常情况下都建议使用 ArrayList 集合,只有在涉及到线程安全问题时才使用 Vector 集合。
(3)LinkedList 集合
LinkedList 集合是基于链表实现的,因此在需要频繁的增删操作时效率明显要高于 ArrayList,但是它的随机访问效率远不及 ArrayList。LinkedList 集合除了包含 Collection 接口和 List 接口中的所有方法之外,还特别提供下表所示的操作首尾元素的方法。如果只在首尾频繁操作元素,或要实现栈和队列的操作,则可以考虑使用该集合。
方法名称 | 说明 |
void addFirst(E e) | 将指定元素添加到此集合的开头 |
void addLast(E e) | 将指定元素添加到此集合的末尾 |
E getFirst() | 返回此集合的第一个元素 |
E getLast() | 返回此集合的最后一个元素 |
E removeFirst() | 删除此集合中的第一个元素 |
E removeLast() | 删除此集合中的最后一个元素 |
2.Set 集合
Set 继承了 Collection 接口,是一个无序、不可重复的集合。集合中的元素没有其固定的排序顺序,不能包含重复的元素,只能有一个为 null 的元素。Set 集合中除了没有与索引有关的方法外其余方法与 List 集合基本一样,在此不再列举。
(1)HashSet 集合
HashSet 集合是基于哈希表结构实现的 Set 集合,其底层通过 HashMap 实现,它是线程不安全的。正是由于哈希表的特点,致使其无法按照插入顺序来遍历集合。
HashSet 集合在每次存储元素时,都会先调用 hashCode ( ) 方法获取元素的哈希编码,根据 hashCode ( ) 找到其存储位置,这样就大大缩短了查找时间;然后再通过 equals ( ) 方法判断此位置的元素链中是否包含相同元素,只有在没有相同元素的时候才会将该元素存储。若链中有相同元素,则会用当前要存储的元素覆盖相同元素。
下面用一个案例来说明:
① 声明一个 Person 类,让它默认继承 Object 类的 HashCode ( ) 方法与 equals ( ) 方法。
public class Person {
String name;
Integer age;
public Person(String name, Integer age) {
= name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
② 实例化两个同名不同岁的 Person 对象进行比较,可以看到这两个对象是完全不相同的。
public static void main(String[] args) {
Person p1 = new Person("张三",25);
Person p2 = new Person("张三",30);
//打印哈希编码
System.out.println(p1.hashCode());//460141958
System.out.println(p2.hashCode());//1163157884
//比较地址
System.out.println(p1==p2);//false
//比较值(默认还是比较地址)
System.out.println(p1.equals(p2));//false
Set set = new HashSet();
set.add(p1);
set.add(p2);
System.out.println(set);
//[Person{name='张三', age=30}, Person{name='张三', age=25}]
}
③ 重写 HashCode ( ) 方法与 equals ( ) 方法。
@Override
public boolean equals(Object o) {
if (this == o) return true;//地址是否相等
//是否是同类型
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return person.name.equals(name);
}
@Override
public int hashCode() {
return name.hashCode();
}
④ 再次比较:此时尽管两个对象内存地址不同,但 HashSet 认为它们是同一个对象,并用后来的对象覆盖了前面的。
public static void main(String[] args) {
Person p1 = new Person("张三",25);
Person p2 = new Person("张三",30);
//打印哈希编码
System.out.println(p1.hashCode());//774889
System.out.println(p2.hashCode());//774889
//比较地址
System.out.println(p1==p2);//false
//比较值name
System.out.println(p1.equals(p2));//true
Set set = new HashSet();
set.add(p1);
set.add(p2);
System.out.println(set);
//[Person{name='张三', age=25}]
}
(2)LinkedHashSet 集合
LinkedHashSet 继承自 HashSet ,内部由 LinkedHashMap 实现,保留了 HashSet 的的所有特征,并且还能够保证插入顺序与迭代顺序的一致性。那么 LinkedHashSet 的一致性是如何实现的呢?
其实就是把 HashSet 中的哈希表结构链表部分结点新加了两个指针。其中 before 指针指向上一个插入的元素,after 指针指向下一个插入的元素,最后把头指针指向第一个插入的元素,最后一个元素指向头指针。迭代时只需要根据头指针依次迭代每个结点即可按插入顺序迭代。
因为在迭代时是直接从头结点开始,无需再访问数组中的地址,因此迭代效率会优于 HashSet。但由于每次增加删除元素时都需要不断地修改 befor 和 after 指针,因此增删操作系统开销会大于 HashSet。
(3)TreeSet 集合
TreeSet 基于红黑树结构同时实现了 Set 接口和 SortedSet 接口。SortedSet 接口是 Set 接口的子接口,可以实现对集合进行自然排序(升序),因此使用 TreeSet 类实现的 Set 接口默认情况下是自然排序的。
TreeSet 只能对实现了 Comparable 接口的类对象进行排序,因为 Comparable 接口中有一个 compareTo ( Object o ) 方法用于比较两个对象的大小。下面是 JDK 类库中实现 Comparable 接口的类,以及这些类对象的比较方式。
类 | 比较方式 | |
包装类(BigDecimal、Biglnteger、 Byte、Double、 Float、Integer、Long 及 Short) | 按数字大小比较 | |
Character | 按字符的 Unicode 值的数字大小比较 | |
String | 按字符串中字符的 Unicode 值的数字大小比较 |
下面是TreeSet 一些独有的方法:
方法名称 | 说明 |
E first() | 返回此集合中的第一个元素 |
E last() | 返回此集合中的最后一个元素 |
E poolFirst() | 获取并移除此集合中的第一个元素 |
E poolLast() | 获取并移除此集合中的最后一个元素 |
SortedSet<E> subSet(E fromElement,E toElement) | 返回一个新的集合,新集合包含原集合中 fromElement 对象与 toElement 对象之间的所有对象。包含 fromElement 对象,不包含 toElement 对象 |
SortedSet<E> headSet(E toElement) | 返回一个新的集合,新集合包含原集合中 toElement 对象之前的所有对象。 不包含 toElement 对象 |
SortedSet<E> tailSet(E fromElement) | 返回一个新的集合,新集合包含原集合中 fromElement 对象之后的所有对 象。包含 fromElement 对象 |
下面用一个案例来说明:
① 没有实现 Comparable 接口的 Person 对象放入TreeSet 集合。
Person p1 = new Person("张三",25);
Person p2 = new Person("李四",30);
Person p3 = new Person("王五",30);
Set set = new TreeSet();
set.add(p1);
set.add(p2);
set.add(p3);
System.out.println(set);
直接报错:类型不兼容(没有实现Comparable接口)
Exception in thread "main" java.lang.ClassCastException
② Person 类实现 Comparable 接口,并比较年龄大小。
@Override
public int compareTo(Person o) {
return this.age - o.age;
}
public static void main(String[] args) {
Person p1 = new Person("王五",30);
Person p2 = new Person("赵六",80);
Person p3 = new Person("张三",25);
Person p4 = new Person("李四",30);
Set set = new TreeSet();
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
System.out.println(set);
}
结果:
[Person{name='张三', age=25}, Person{name='李四', age=30}, Person{name='赵六', age=80}]
可以看到,在添加元素时 TreeSet 就自动按年龄升序排列元素,而年龄相等的则认为是相同元素并替换了原有元素。