目录

1.List 集合

2.Set 集合


1.List 集合

List 继承了 Collection 接口,是一个有序、可重复的集合,集合中每个元素都有其对应的顺序索引。List 集合允许使用重复元素,可以通过索引来访问指定位置的集合元素,可以精确的将元素插入指定位置。

(1) List 集合中常用的一些方法:

 void

add(int index, E           在列表的指定位置插入指定元素(可选操作)。

 boolean

addAll(int index, Collection<? extends E> c)           将指定 collection 中的所有元素都插入到列表中的指定位置(可选操作)。

 E

get(int index)           返回列表中指定位置的元素。

 int

indexOf(Object           返回此列表中第一次出现的指定元素的索引;如果此列表不包含该元素,则返回 -1。

 int

lastIndexOf(Object           返回此列表中最后出现的指定元素的索引;如果列表不包含此元素,则返回 -1。

 ListIterator<E>

listIterator()           返回此列表元素的列表迭代器(按适当顺序)。

 ListIterator<E>

listIterator(int index)           返回列表中元素的列表迭代器(按适当顺序),从列表的指定位置开始。

 E

remove(int index)           移除列表中指定位置的元素(可选操作)。

 E

set(int index, E           用指定元素替换列表中指定位置的元素(可选操作)。

 List<E>

subList(int fromIndex, int toIndex)           返回列表中指定的 fromIndex(包括 )和 toIndex(不包括)之间的部分视图。

(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 指针指向下一个插入的元素,最后把头指针指向第一个插入的元素,最后一个元素指向头指针。迭代时只需要根据头指针依次迭代每个结点即可按插入顺序迭代。

java 线程安全的带顺序的list_指定位置

因为在迭代时是直接从头结点开始,无需再访问数组中的地址,因此迭代效率会优于 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 就自动按年龄升序排列元素,而年龄相等的则认为是相同元素并替换了原有元素。