集合

组成

1.Set:无序、不可重复
2.List:有序,可重复
3.Queue:队列,先进先出
4.Map:映射(键值对)


Collection

Set、List、Queue是Collection的子接口。


Collection接口中定义的一些方法

方法 说明
boolean add(E e) 向集合中添加一个元素,如果集合对象被添加操作改变了,返回true
boolean addAll(Collection<?> c) 把集合c中的所有元素加到指定集合中
void clear() 清除集合中的所有元素
boolean contains(Object o) 返回集合中是否包含指定元素
boolean containsAll(Collection<?> c) 返回集合中是否包含集合c中的所有元素
boolean isEmpty() 返回集合是否为空
Iterator iterator() 返回一个Iterator对象,一般用于遍历集合
boolean remove(Object o) 删除集合中的指定元素,只删除第一个符合条件的元素,删除成功返回true
boolean removeAll(Collection<?> c) 删除集合中在集合c中的所有元素,删除一个或一个以上元素,返回true
boolean removeIf(Predicate < ? super E > filter) 从这个集合中删除filter返回true的所有元素。如果由于这个调用改变了集合,返回true
boolean retainAll(Collection<?> c) 删除集合中不在集合c中的所有元素
int size() 返回集合中元素的个数
< T > T[] toArray() 返回这个集合的对象数组
< T > T[] toArray(T[] a) 返回这个集合的对象数组,如果a足够大,就将集合中的元素填入a,剩余的空间补null。否则,分配一个新的数组,其成员类型于a的成员类型相同,长度等于集合的大小,并填充集合元素(这时,并不会为a赋值)

集合的遍历

Collection继承了Iterable接口,所以可以使用foreach遍历。也可以先调用iterator()方法,得到一个Iterator对象,利用Iterator来遍历。


例子如下

public class Test {

    public static void main(String args[]) throws Exception {
        List<String> list = new ArrayList<>();
        list.add("大娃");
        list.add("二娃");
        list.add("三娃");

		//foreach循环
        for (String s : list) {
            System.out.println(s);
        }

        Iterator<String> iterator = list.iterator();

        //Iterator遍历
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        iterator = list.iterator();

        //java8新增,可以用lambda表达式
        iterator.forEachRemaining(s -> System.out.println(s));

    }
}

Iterator接口

方法 说明
boolean hasNext() 判断是否还有下一个元素
E next() 返回下一个元素
default void remove() 接口默认方法,删除上一次next的返回元素
default void forEachRemaining(Consumer < ? super E > action) 接口的默认方法,可使用lambda遍历集合

Iterator接口

public interface Iterator<E> {
    /**
     * 如果还有下一个元素的话,就返回true
     */
    boolean hasNext();

    /**
     * 返回下一个元素,如果没有下一个元素的话,将会抛出 NoSuchElementException 异常
     */
    E next();

    /**
     * 删除集合中的元素,每调用一次 next 后才能调用一次 remove 。连续调用两次remove或者
     * 没有先调用next,将会抛出 IllegalStateException 异常。如果不支持remove,调用了
     * 将会抛出 UnsupportedOperationException。该方法的默认实现是直接抛出 UnsupportedOperationException 异常。
     */
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }

    /**
     * 对迭代器中剩下的元素执行指定的动作,直到所有元素都执行过了或者抛出了异常。
     * 执行的顺序是按照迭代器中指定的顺序(如果有指定的顺序的话)
     */
    default void forEachRemaining(Consumer < ? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}

在使用迭代器的时候要注意,可以根据需要给容器附加许多的迭代器,但是这些迭代器只能读取,不要修改集合结构(增加或者删除,如果只是修改元素的内容的话,不算为修改集合)。


如下 ```java public class Test {
public static void main(String args[]) throws Exception {
    List<String> list = new ArrayList<> ();
    list.add("大娃");
    list.add("二娃");
    list.add("三娃");

    Iterator<String> iterator = list.listIterator();
    Iterator<String> iterator1 = list.listIterator();

    iterator1.next();
    iterator1.remove();
    iterator.next();    //这里会抛出ConcurrentModificationException异常

}

}


<br/>


### <span id="Iterable">Iterable</span>接口

```java
/**
 * 实现这个接口就能使用for-each循环~
 */
public interface Iterable<T> {
    /**
     * 返回一个内部元素类型为T的Iterator
     */
    Iterator<T> iterator();

    /**
     * java8 出现的默认方法,子类可以不用实现这个方法
     * 遍历内部元素,并执行指定操作
     */
    default void forEach(Consumer< ? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }

    /**
     * 也是一个默认方法,返回一个可并行遍历的迭代器
     */
    default Spliterator<T> spliterator() {
        return Spliterators.spliteratorUnknownSize(iterator(), 0);
    }
}

Set

Set直接继承于Collection,两者几乎相同,只不过Set表示的是集,所以有一些自己的特点。Set集合不允许包含相同的元素,使用add添加一个已经存在的元素的时候会返回false。


1.HashSet

1.无序
2.不同步,若果多个线程同时访问一个HashSet,必须通过代码来保证同步
3.集合元素可以为null

HashSet是通过计算对象的hashCode值,来决定对象的存储位置的。如果有两个元素用equals方法比较,返回true,但是hashCode的返回值不相等,那么这两个元素会被放在两个不同的位置,也就是说HashSet并不认为这两个元素相等。所以,我们在重写equals方法的时候,也应该重写hashCode方法,并保证equals方法的返回值与hashCode的返回值是对应的。


HashSet的构造器

//从中我们可以看出,HashSet实际上是用HashMap实现的。
public HashSet() {
    map = new HashMap<>();
}

public HashSet(Collection < ? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}

//构造一个具有指定容量(桶数)的散列集
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}

//构造一个具有指定容量和装填因子的散列集(装填因子范围0.0~1.0)
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}

在java中,散列表用链表数组实现,每个列表被称为桶
装填因子,决定何时对散列集再散列。即创建一个容量更大的散列集。


什么是桶

hash表中可以存放元素的位置,称为“桶”。通常情况下,一个桶里只存放一个元素,但是如果出现hash冲突的话,一个桶里面就可能存放对个元素,并且这些元素是通过链表存储的,所以一个桶里面的元素(即hash值相同)只能通过顺序搜索。


2.LinkedHashSet

基本上和HashSet一样,不过LinkedHashSet集合里面的元素是按照插入的顺序来存储的,用链表来维护这个次序。


3.TreeSet

TreeSet是一个有序的集合,即插入元素的时候,会自动排序(自然排序或者自定义排序)
当把一个对象加入TreeSet集合的时候,调用compareTo方法与容器中的其他对象比较大小,然后根据红黑树结构找到存储位置,也就是说TreeSet根据compareTo的返回结果来判断两个对象是否相等。所以,要使用TreeSet来存放一个类的对象,这个类就必须是可比较的,也就是说需要实现Comparable接口。


TreeSet的构造器

public TreeSet() {
    this(new TreeMap<E,Object>());
}

//自定义比较规则
public TreeSet(Comparator< ? super E> comparator) {
    this(new TreeMap<>(comparator));
}

public TreeSet(Collection< ? extends E> c) {
    this();
    addAll(c);
}

public TreeSet(SortedSet<E> s) {
    this(s.comparator());
    addAll(s);
}

TreeSet实现类中的一些方法

方法 说明
E first() 返回集合中的第一个元素
E last() 返回集合中的最后一个元素
E lower(E e) 返回集合中位于e之前的那个元素
E higher(E e) 返回集合中位于e之后的那个元素
SortedSet< E > subSet(E from, E to) 返回from到to之间的元素集合,不含to元素
SortedSet< E > headSet(E to) 返回小于to的元素集合
SortedSet< E > tailSet(E from) 返回大于等于from的元素集合

TreeSet的自定义排序规则

public class Test {

    public static void main(String args[]) throws Exception {
        //默认顺序
        TreeSet<Student> students = new TreeSet<>();
        students.add(new Student(3, "大娃"));
        students.add(new Student(2, "二娃"));
        students.add(new Student(1, "三娃"));

        for (Student s : students)
            System.out.println(s);


        //逆序
        TreeSet<Student> students1 = new TreeSet<>(Comparator.reverseOrder());
        students1.addAll(students);

        for (Student s : students1)
            System.out.println(s);

    }

}

class Student implements Comparable<Student> {

    private int age;
    private String name;

    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }

    public String getName() {
        return this.name;
    }

    @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;
        return this.age == other.getAge() && this.name.equals(other.getName());
    }

    @Override
    public int hashCode() {
        return Objects.hash(this.age, this.name);
    }

    @Override
    public int compareTo(Student o) {
        int result = Integer.compare(this.age, o.getAge());
        return result != 0 ? result : this.getName().compareTo(o.getName());
    }

    @Override
    public String toString() {
        return this.name + ": " + this.age;
    }
}

4.EnumSet

EnumSet集合中的所有元素都必须是指定的枚举类型的枚举值,EnumSet以枚举值在枚举类中定义的顺序来决定集合中元素的顺序。


EnumSet实现类中的一些方法

方法(下列的方法都为静态的) 说明
EnumSet < E > allOf(Class < E > elementType) 创建一个包含指定枚举类型的所有枚举值的EnumSet集合
EnumSet < E > complementOf(EnumSet < E > s) 创建一个包含指定枚举类型的所有不在s中的枚举值的EnumSet集合,
EnumSet < E > noneOf(Class < E > elementType) 创建一个不包含任何枚举实例的指定枚举类型的EnumSet集合
EnumSet < E > copyOf(EnumSet < E > s) 创建一个和s完全相同的新EnumSet集合
EnumSet < E > copyOf(Collection < E > c) 使用一个Collection来创建一个EnumSet集合
EnumSet < E > of(E first, E... rest) 创建一个包含一个或者多个枚举值的EnumSet集合
EnumSet < E > range(E from, E to) 创建一个包含从from到to范围内的所有枚举值的EnumSet集合

List

有序、可重复的集合,集合中的每个元素有对应的索引


List接口中定义的一些方法(List继承于Collection,不再列举Collection中的方法)

方法 说明
E get(int index) 返回指定位置的元素
E set(int index, E element) 设置指定位置的元素
void add(int index, E element) 往指定位置上插入元素
boolean addAll(int index, Collection < ? extends E > c) 往指定位置上插入c的所有元素
E remove(int index) 删除指定位置上的元素
int indexOf(Object o) 返回o第一次出现的位置
int lastIndexOf(Object o) 返回o最后一次出现的位置
List < E > subList(int fromIndex, int toIndex) 返回从fromIndex位置(包含)到toIndex位置(不含)的所有元素的集合
default void replaceAll(UnaryOperator < E > operator) 接口默认方法,用新的规则得到的新元素替换旧元素
default void sort(Comparator < ? super E > c) 接口默认方法,根据c对集合进行排序
ListIterator < E > listIterator() 返回一个ListIterator
ListIterator < E > listIterator(int index) 返回一个从指定位置算起的ListIterator

ListIterator接口

继承于Iterator,并在Iterator的基础上新增了一些方法。特别是add方法,在Iterator中没有这个方法,add方法使实现了ListIterator接口的类,能够在使用迭代器的过程中往集合里加入新的元素。


方法 说明
boolean hasPrevious() 是否还有上一个元素
E previous() 返回上一个元素
int nextIndex() 返回next的位置
int previousIndex() 返回previous的位置
void set(E e) 设置next或者previous位置上的元素值
void add(E e) 在next或者previous的位置插入新元素

1.ArrayList

基于数组实现的List类,Arraylist封装了一个动态的、允许再分配的数组。Arraylist使用initialCapacity来设置数组的长度(默认是10),当Arraylist中的元素超过数组的长度的时候,initialCapacity会自动增加。线程不安全。


我们也可以重新分配数组的长度,Arraylist提供了下面两个方法

方法 说明
void ensureCapacity(int minCapacity) 将数组长度增加到大于或者等于minCapacity的值
void trimToSize() 调整数组长度为当前元素的个数

2.Vector

老式集合,不太推荐使用。线程安全


3.Stack

Stack继承于Vector,用来模拟栈,但性能较差所以也不推荐使用。要模拟栈的操作时可以考虑ArrayDeque


Queue

队列,先进先出


Queue接口中定义的一些方法

方法 说明
boolean offer(E e) 将指定元素加入队尾
boolean add(E e) 将指定元素加入队尾
E peek() 获取队列头部元素,但不删除。队列为空则返回null
E element() 获取队列头部元素,但不删除
E poll() 获取队列头部元素,并删除。队列为空则返回null
E remove() 获取队列头部元素并删除

1.PriorityQueue

PriorityQueue元素的顺序是按照元素的大小进行保存的。创建PriorityQueue的时候可以指定是自然排序还是自定义排序。简单来说,PriorityQueue就是在Queue的基础上按照某种规则进行排序。


2.Deque

Queue的子接口,它代表一个双端队列。


Deque接口中定义的一些方法

方法 说明
void addFirst(E e) 将指定元素插入队列头部
void addLast(E e) 将指定元素插入队列尾部
boolean offerFirst(E e) 将指定元素插入队列头部
boolean offerLast(E e) 将指定元素插入队列尾部
E removeFirst() 获取并删除队列的第一个元素
E removeLast() 获取并删除队列的最后一个元素
E pollFirst() 获取并删除队列的第一个元素
E pollLast() 获取并删除队列的最后一个元素
E getFirst() 获取但不删除队列的第一个元素
E getLast() 获取但不删除队列的最后一个元素
E peekFirst() 获取但不删除队列的第一个元素
E peekLast() 获取但不删除队列的最后一个元素
boolean removeFirstOccurrence(Object o) 删除队列中第一次出现的元素o
boolean removeLastOccurrence(Object o) 删除队列中最后一次出现的元素o
void push(E e) 模拟栈,入栈
E pop() 模拟栈,出栈
Iterator< E > descendingIterator() 返回逆序的队列的Iterator

2.1 ArrayDeque

Deque的实现类,基于数值实现的双端队列,创建的时候可以指定数组的长度。


3.LinkedList

LinkedList同时实现了List接口和Deque接口,所以拥有List和Deque的特性。嗯,很强大就是了。由于是用链表实现的所以在插入、删除元素的时候,性能较好。但是随机访问元素的时候就比不上Arraylist和ArrayDeque了。


Map

存放着一组组键值对(key-value),key不允许重复


Map接口中的一些方法

方法 说明
int size() 返回集合中的键值对个数
boolean isEmpty() 判断集合是否为空
boolean containsKey(Object key) 判断集合中是否包含指定key
boolean containsValue(Object value) 判断集合中是否包含一个或者多个value
V get(Object key) 返回指定key对应的value
default V getOrDefault(Object key, V defaultValue) 返回指定key的value值,若不存在则返回default
V put(K key, V value) 添加一个新的键值对(会覆盖集合中key相同的键值对)
void putAll(Map < ? extends K, ? extends V > m) 将m中的键值对添加到集合中
V remove(Object key) 删除指定key的键值对
void clear() 清空集合
Set keySet() 返回由集合中所有key组成的Set集合
Collection < V > values() 返回由集合中所有value组成的Collection集合
Set < Map.Entry < K, V > > entrySet() 返回由Map中所有键值对组成的集合

Entry(Map的内部类)的一些方法

方法 说明
K getKey() 返回该entry里面包含的key
V getValue() 返回该entry里面包含的value
V setValue(V value) 设置该entry里面的value值

HashMap

和HashSet基本相似,都是用Hash值来保存元素的。


HashMap的构造方法

方法 说明
public HashMap() 不带参数的构造方法
public HashMap(int initialCapacity) 指定容量的构造方法
public HashMap(int initialCapacity, float loadFactor) 指定容量和装载因子的构造方法
public HashMap(Map < ? extends K, ? extends V > m) 用另一个Map构造一个HashMap

Hashtable

和HashMap基本一样,事实上HashMap是Hashtable的轻量级实现。


HashMap和Hashtable的区别

1.Hashtable是线程安全的,而HashMap是线程不安全的
2.Hashtable不允许使用null作为key和value,而HashMap是可以使用null作为key和value的


使用HashMap和Hashtable的一些注意点

1.要用HashMap和Hashtable来存放某个类的实例。则这个类必须实现hashCode和equals方法,并且保证两个方法的判断标准是一致的。
2.尽量不要使用可变对象作为key,再不行,也要保证尽量不修改作为key的对象,以免造成hash的混乱(这一点和HashSet是一样的)


LinkedHashMap

在HashMap的基础上,运用链表来实现,元素的有序存储。


相比HashMap,多了一个可以指定存储的顺序方式

方法 说明
public LinkedHashMap(int initialCapacity,float loadFactor,boolean accessOrder) accessOrder默认false表示按插入顺序,true则表示按访问顺序

Properties

Hashtable的子类,在处理属性文件的时候比较方便,相当于一个key和value都是String的map。Properties类提供了读写属性文件的方法


SortedMap接口

该接口继承于Map。实现了该接口的类,是有序的

SortedMap接口的一些方法

方法 说明
Comparator < ? super K > comparator() 返回对键进行排序的比较器,如果键是用Comparable接口的compareTo方法进行比较的,则返回null
SortedMap<K,V> subMap(K fromKey, K toKey) 返回从fromKey到toKey(不含)的集合
SortedMap<K,V> headMap(K toKey) 返回从第一个元素到toKey(不含)的集合
SortedMap<K,V> tailMap(K fromKey) 返回从fromKey到集合最后一个元素的集合
K firstKey() 返回第一个元素的key
K lastKey() 返回最后一个元素的key

TreeMap

实现了SortedMap接口,和TreeSet相同,采用红黑树数据结构,根据key进行排序,也有两种排序方式
1.自然排序:TreeMap的所有key都必须实现Comparable接口
2.定制排序:创建TreeMap的时候,传入一个comparator对象,让该对象负责对TreeMap中的所有key进行排序。此时,不需要实现Comparable接口。


TreeMap的构造方法

方法 说明
public TreeMap() 创建一个空的TreeMap对象
public TreeMap(Comparator< ? super K > comparator) 指定TreeMap的比较器
public TreeMap(Map< ? extends K, ? extends V > m) 用一个Map对象构造TreeMap对象
public TreeMap(SortedMap< K, ? extends V > m) 用一个SortedMap对象构造TreeMap对象,且比较器与SortedMap相同

WeakHashMap

和HashMap的用法基本相似,不过WeakHashMap的key只保留对实际对象的弱引用,这就意味着如果WeakHashMap对象的key所引用的对象没有被其他强引用变量所引用,则这些key所引用的对象可能会被垃圾回收,WeakHashMap也可能自动删除这些key所对应的key-value对。HashMap的key保留了对实际对象的强引用,所以不会有这些问题。


IdentityHashMap

和HashMap基本相似,不过IdentityHashMap只有当key1 == key2返回true时,才会认为两个key是相等的,也就是说,IdentityHashMap认为只有两个key是同一个对象,这两个key才是相等的。而HashMap则只要两个key的内容相等,即调用equals和hashCode时返回t相同的值,就认为两个key是相等的。


EnumMap

EnumMap是一个与枚举类一起使用的类,创建EnumMap对象时,必须显式指定一个枚举类,将该EnumMap与指定枚举类关联,EnumMap中key的值只能是枚举类实例的值。


posted @ 2018-05-01 17:48  _weirdly  阅读(131)  评论(0编辑  收藏  举报