集合
组成
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 |
返回由集合中所有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的值只能是枚举类实例的值。