目录
1.前言:
2.集合类图概述:
3.接口及类的详解:
1.Iterator接口
2.Iterable接口
3.Collection接口
4.List接口及其实现类
一.ArrayList 类
二.LinkedList类
三.Vector类
四.Stack类
5.Set接口及其实现类
一.HashSet和LinkedHashSet
二.TreeSet类
6.Queue接口及其实现类
7.Deque接口及其实现类
一.ArrayDeque类
二.LinkedList类(此处用来实现双端队列)
8.Map接口及其实现类
一.HashMap类
二.LinkedHashMap类
三.TreeMap类
在jdk1.2之前,Java就提供了诸如:Dictionary, Vector, Stack,这些类用来存储和操作对象组。但是它们缺少一个统一的主题。于是在jdk1.2之后便出现了集合框架,在jdk1.5上又添加了Iterable接口用来实现集合的迭代, 整个集合框架就围绕一组标准接口(例如Set,List) 而设计。你可以直接使用这些接口的标准实现,诸如: LinkedList, HashSet, 和 TreeSet等,除此之外你也可以通过这些接口实现自己的集合。
读者还应注意Java中集合与数组的区别:
(注:1.此图只显示类常用接口和类,省略了部分中间类,将在下文逐一说明。2.Map族独立于collection,但属于集合框架中的一部分)
首先要注意一点,Iterator接口和Iterable接口没有半毛钱的继承实现关系,唯一的关系是,在Iterable接口中有一个可返回Iterator类型的方法iterator()。 Iterator是一个集合的迭代器,通俗的说,它可将集合中的元素逐次取出,常用方法如下:
boolean hasNext() | 如果还可以迭代下一个元素,则返回true |
E next() | 返回迭代中的下一个元素 |
Collection族下的类都间接实现此接口,实现此接口允许对象成为“for-each loop”语句的目标
接口中两个最常用的方法如下:
default void forEach(Consumer super T> action | 对 Iterable 的每个迭代元素执行给定的操作,传入一个函数的引用 |
Iterator |
返回类型为 T 的迭代器 |
代码示例:
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add(520);
arrayList.add(true);
arrayList.add(new Date());
arrayList.forEach(System.out::println);
Iterator iterator=arrayList.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
运行结果:
集合层次结构中的根接口 。 集合表示一组被称为其元素的对象。 一些集合允许重复元素,而其他集合不允许。JDK不提供此接口的任何直接实现:它提供了更具体的子接口的实现,如Set和List 等,常用扩展方法如下:
boolean add(E e) | 添加元素 |
boolean addAll(Collection extends E> c) | 将指定集合中的所有元素添加到该集合中 |
void clear() | 删除此集合中的所有元素 |
boolean contains(Object o) | 判断此集合是否包含指定元素,对比方式是equals(Object o) |
boolean isEmpty() | 判断集合是否为空 |
boolean remove(Object o) | 成功返回true,如果删除失败或者该元素不存在,返回false |
boolean removeAll(Collection> c) | 删除本集合中与给定集合的交集 |
boolean retainAll(Collection> c) | 保存本集合中与给定集合的交集 |
int size() | 返回此集合中的元素数 |
Object[] toArray() | 返回一个包含此集合中所有元素的对象数组 |
首先说明List与Set的区别:
List | Set |
---|---|
元素有序(存入取出位置不变) | 元素无序 |
元素存在唯一的索引值 | 不存在索引 |
允许重复元素 | 不允许重复元素 |
在Collection基础上,List扩展的如下方法:
void add(int index,E element) | 将元素插入指定的位置 |
boolean addAll(int index,Collection extend E> c) | 按指定集合的迭代器顺序将其添加到本元素的指定位置 |
E get(int index) | 返回此列表指定位置的元素 |
int indexOf(Object o) int lastIndexOf(Object o) |
返回此列表中指定元素第一次(最后一次)出现的位置 如果不存在则返回-1 |
ListIterator ListIterator |
返回此列表中的列表迭代器 从指定位置开始返回迭代器 |
E remove(int index) | 删除指定位置的元素,并返回到该元素 |
default void sort(Comparator super E> c) | 使用随附的 Comparator排序此列表来比较元素。 |
List |
返回此列表中指定的 fromIndex (含)和 toIndex之间的视图。 |
注:ListIterator 接口继承自 Iterator,它允许沿任一方向遍历列表
代码示例:
public static void main(String[] args) {
ArrayList arrayList=new ArrayList();
arrayList.add(520);
arrayList.add(43);
arrayList.add(321);
arrayList.add(0,999);
arrayList.add(334);
System.out.println("原始数组:");
arrayList.forEach(System.out::println);
System.out.println("获取第3个元素:"+arrayList.get(2));
System.out.println("a的位置:"+arrayList.indexOf(520));
System.out.println("排序后:");
arrayList.sort(Comparator.comparing(Integer::intValue));
arrayList.forEach(System.out::println);
System.out.println("ListIterator迭代:");
ListIterator listIterator=arrayList.listIterator();
while(listIterator.hasNext()){
System.out.println(listIterator.next());
}
System.out.println("删除第3个位置的元素:"+arrayList.remove(2));
System.out.println("新建数组内容为");
List list=arrayList.subList(1,3);
list.forEach(System.out::println);
}
运行结果:
实现List接口,并允许所有元素,包括null,每个ArrayList实例都有一个容量 。 容量是用于存储列表中的元素的数组的大小。 它总是至少与列表大小一样大。 当元素添加到ArrayList时,其容量会自动增长。 没有规定增长的细节
此处说明:
ArrrayList与Vector的区别:1.ArrayList不是线程同步的,而Vector是线程同步的。2.ArrayList默认扩容量是原来的0.5倍,而Vector的默认扩容是原来的一倍
ArrayList与LinkedList的区别:ArrayList由数组备份。LinkedList由链接备份,并且LinkedList还可以用作队列。访问ArrayList中的元素更快,因为它的底层结构是数组,而当集合要频繁的进行增删,LinkedList 的性能优于 ArrayList 。
继承关系:
构造方法:
ArrayList() | 构造一个初始容量为十的空列表。 |
ArrayList(Collection extends E> c) | 构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。 |
ArrayList(int initialCapacity) | 构造具有指定初始容量的空列表。 |
扩展方法:
Object clone() | 返回此 ArrayList实例的浅拷贝(关于深浅拷贝的区别见下文) |
void ensureCapacity(int minCapacity) | 增加此 ArrayList实例的容量,以确保它可以至少保存最小容量参数指定的元素数 |
void trimToSize() | 修改这个 ArrayList实例的容量是列表的当前大小 |
void replaceAll(UnaryOperator |
将该列表的每个元素替换为将该运算符应用于该元素的结果 |
E set(int index, E element) | 用指定的元素替换此列表中指定位置的元素 |
注:浅拷贝:被复制对象与原对象在内存中的引用是同一个对象
深拷贝:在内存中新开辟一块空间,把要复制的对象所引用的对象都复制了一遍,对现在对象的修改不会影响原有的对象。
代码示例:
public class Main {
public static void main(String[] args) {
ArrayList arrayList = new ArrayList(100);
People people1 = new People(66);
arrayList.add(people1);
arrayList.add(new People(47));
arrayList.add(new People(33));
arrayList.set(2,new People(55));
people1.setAge(100);
arrayList.forEach(System.out::println);
System.out.println("拷贝数组");
ArrayList clonearray = (ArrayList) arrayList.clone();
clonearray.forEach(System.out::println);
System.out.println("将people的值改变后:");
people1.setAge(888);
clonearray.forEach(System.out::println);
}
}
class People {
int age;
public People(int age) {
this.age = age;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "People{" +
"age=" + age +
'}';
}
}
运行结果:
LinkedList是非线程同步的,LinkedList与ArrayList的区别上文已讲,而当集合要频繁的进行增删,建议使用LinkedList提高性能,由于LinkeList还是同时实现了List和Deque接口,所以它还可以用来表示队列,具体实现在下文Queue接口和Deque接口中
类图结构:
Vector在共功能上与ArrayList相似,如果不需要线程安全的实现,建议使用ArrayList代替Vector
构造方法如下:
Vector() | 构造一个空向量,使其内部数据数组的大小为 10 ,标准容量增量为零 |
Vector(Collection extends E> c) | 构造一个包含指定集合元素的向量,按照集合的迭代器返回的顺序。 |
Vector(int initialCapacity) | 构造具有指定初始容量并且其容量增量等于零的空向量。 |
Vector(int initialCapacity, int capacityIncrement) | 构造具有指定的初始容量和容量增量的空向量。 |
成员方法略
Stack是一个后进先出的栈(LIFO),但Deque接口及其实现提供了更完整和一致的LIFO堆栈操作集,这些接口应优先于此类。 例如: Deque
Stack继承自Vector,扩展方法如下:
boolean empty() | 测试此堆栈是否为空。 |
E peek() | 查看此堆栈顶部的对象,而不从堆栈中删除它。 |
E pop() | 删除此堆栈顶部的对象,并将该对象作为此函数的值返回。 |
E push(E item) | 将项目推送到此堆栈的顶部。 |
int search(Object o) | 返回一个对象在此堆栈上的基于1的位置 |
代码示例:
private static void showpush(Stack st, int a) {//把项压入栈顶
st.push(new Integer(a));
System.out.println("push(" + a + ")");
System.out.println("stack: " + st);
}
private static void showpop(Stack st) {//移除堆栈顶部的对象并作为此函数的值返回该对象
Integer a = (Integer) st.pop();
System.out.println(a);
System.out.println("after pop stack: " + st);
}
private static void showpeek(Stack stack){
Integer a=(Integer) stack.peek();
System.out.println(a);
System.out.println("after peek stack:"+stack);
}
public static void main(String args[]) {
Stack st = new Stack();
System.out.println("stack: " + st);
showpush(st, 42);
showpush(st, 66);
showpush(st, 99);
showpeek(st);
showpop(st);
showpop(st);
showpop(st);
try {
showpop(st);
} catch (EmptyStackException e) {
System.out.println("empty stack");
}
}
运行结果:
Set集合是唯一元素集合,最多只有一个null值,当向集合添加重复元素时,它们将被忽略,元素是否相同是通过equals()方法实现的
区别:HashSet
不保证顺序元素。 LinkedHashSet
在插入元素时保持元素顺序
代码示例:
public static void main(String[] args) {
Set hashSet=new HashSet();
hashSet.add("XML");
hashSet.add("HTML");
hashSet.add("SQL");
hashSet.add(null);
hashSet.add("HTML");
hashSet.add(null);
hashSet.add("JAVA");
System.out.println("hashSet:"+hashSet);
HashSet linkHashSet=new LinkedHashSet<>();
linkHashSet.add("XML");
linkHashSet.add("HTML");
linkHashSet.add("SQL");
linkHashSet.add(null);
linkHashSet.add("HTML");
linkHashSet.add(null);
linkHashSet.add("JAVA");
System.out.println("LinkedHashSet:"+linkHashSet);
}
运行结果:
SortedSet 接口表示Java集合中的排序集合框架,如果它的元素实现了Comparable接口,它将使用compareTo()方法来排序项目。 我们可以称之为自然顺序排序。我们也可以传递一个比较器Comparator做自定义排序。如果指定了 Comparator ,则将Comparator 用于排序并忽略 Comparable 接口。
TreeSet 类是Collection框架中SortedSet接口的一个实现。
类图结构:
代码示例:
public class Main {
public static void main(String[] args) {
/* 按照String中的compareTo的默认比较方法
先比较字符串长度,如果长度一样,则按照字典的顺序依次比较,小写大于大写*/
SortedSet sortedSet=new TreeSet();
sortedSet.add("jav");
sortedSet.add("hdTM");
sortedSet.add("xML");
sortedSet.add("sQL");
System.out.println(sortedSet);
/*
使用Comparable比较器自定义排序关键字
*/
SortedSet students=new TreeSet<>(Comparator.comparing(Student::getAge));
students.add(new Student("Kangkang",13));
students.add(new Student("Mike",11));
students.add(new Student("Jack",18));
students.add(new Student("Lihua",14));
System.out.println("将学生按年龄排序:");
students.forEach(System.out::println);
}
}
class Student{
String name;
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
运行结果:
简单队列用 Queue接口表示,并由LinkedList实现
简单队列队列允许您执行三个基本操作:
Queue接口为三个操作中的每一个定义了两个方法。如果操作不可能,一个方法抛出异常,另一个方法方法返回false或null以指示失败。
方法 | 描述 |
---|---|
boolean add(E e) | 如果可能,向队列中添加一个元素。否则,它抛出异常。 |
boolean offer(E e) | 如果不能添加元素,则将元素添加到队列中,而不抛出异常。 它在失败时返回false,在成功时返回true。 |
E remove() | 删除队列的头。如果队列为空,它会抛出异常。此方法返回已移除的项目。 |
E poll() | 从队列中删除元素。如果队列为空而不是抛出异常,则返回null。 |
Eelement() | 偷看队列的头,而不从队列中删除它。 如果队列为空,它会抛出异常。 |
E peek() | 查看队列,如果队列为空而不是抛出异常,则返回null。 |
代码示例:
public static void main(String[] args) {
Queue queue = new LinkedList<>();
queue.offer("one");
queue.offer("two");
queue.offer("three");
queue.offer("four");
System.out.println(queue);
//出队
System.out.println("使用poll()"+queue.poll());
System.out.println(queue);
System.out.println("使用peek()"+queue.peek());
System.out.println(queue);
}
运行结果:
双端队列或Deque扩展队列以允许元件从两端插入和移除。
Deque 类的实例表示双端队列。 Deque 接口扩展了 Queue 接口。
它可以用作 FIFO队列 或 LIFO队列(堆栈)。
ArrayDeque和LinkedList类是Deque接口的两个实现类。
ArrayDeque 类由数组支持,而 LinkedList 类由链表支持。
如果您使用Deque作为堆栈,则应该使用 ArrayDeque 作为 Deque 实现。
如果使用 Deque 作为FIFO队列,则应该使用 LinkedList 作为 Deque 实现。
常用方法:
E peek() | 检索但不删除栈顶 |
void push(E e) | 将元素压入栈 |
E pop() | 弹出栈顶元素 |
代码示例:
public static void main(String[] args) {
Deque deque=new ArrayDeque();
deque.push("one");
deque.push("two");
deque.push("three");
deque.push("four");
System.out.println(deque);
System.out.println("使用peek():"+deque.peek());
System.out.println(deque);
System.out.println("使用pop():"+deque.pop());
System.out.println(deque);
}
运行结果:
常用方法:
void addFirst(E e) | 在该列表开头插入指定的元素 |
void addLast(E e) | 将指定的元素追加到此列表的末尾 |
E peekFirst() | 检索但不删除此列表的第一个元素,如果此列表为空,则返回 null |
E peekLast() | 检索但不删除此列表的最后一个元素,如果此列表为空,则返回 null |
E pollFirst() | 检索并删除此列表的第一个元素,如果此列表为空,则返回 null |
E pollLast() | 检索并删除此列表的最后一个元素,如果此列表为空,则返回 null |
代码示例:
public static void main(String[] args) {
Deque deque=new LinkedList<>();
deque.addFirst("左一");
deque.addFirst("左二");
deque.addFirst("左三");
deque.addLast("右一");
deque.addLast("右二");
deque.addLast("右三");
System.out.println(deque);
System.out.println("peekFirst():"+deque.peekFirst());
System.out.println(deque);
System.out.println("peekLast():"+deque.peekLast());
System.out.println(deque);
System.out.println("pollFirst():"+deque.pollFirst());
System.out.println(deque);
System.out.println("pollLast():"+deque.pollLast());
System.out.println(deque);
}
运行结果:
敲黑板!:Map是Mapping(映射)的简写,不要以为是数据结构中的 Map(图)
Map是一个集合,其中每个元素表示一个键值对作为
首先我们了解代表Map集合中每个映射条目的内部接口Map.Entry
K getKey() | 获取该条目中的键 |
V getValue() | 获取该条目中的值 |
int hashCode() | 返回此条目的哈希值 |
V setValue(V value) | 使用指定的值替换该条目中的值 |
代码示例:(获取条目的唯一方法是使用Map.entrySet() )
public static void main(String[] args) {
Map map=new HashMap();
map.put(1,"one");
map.put(2,"two");
map.put(3,"three");
map.put(4,"four");
Set entrySet=map.entrySet();
Iterator iterator=entrySet.iterator();
while(iterator.hasNext()){
Map.Entry entry1=iterator.next();
System.out.println(entry1.getKey()+"==="+entry1.getValue());
entry1.setValue("hello world");
}
System.out.println(map);
}
运行结果:
由结果可见,entrySet存储的原Map中的视图,从而实现对Map中元素的迭代和修改,Map接口本身是不具备迭代功能的,除了使用entrySet()外,使用keySet()和values()分别获取键,值的视图也是迭代的不错的选择。
Map接口中的常用方法如下:
V put(K key, V value) | 向集合中添加映射项目 |
void putAll(Map extends K,? extends V> m) | 将指定Map中的值复制到此映射 |
void clear() | 从集合中删除所有的映射 |
boolean containsKey(Object key) | 判断集合中是否包含此键的映射 |
boolean containsValue(Object value) | 判断集合中是否包含此值的映射 |
Set |
返回由Map中的键组成的集合视图 |
Collection |
返回由Map中的值组成的集合视图 |
Set |
返回由Map中的映射条目组成的集合视图 |
Map常用的实现类:
HashMap不保证Map中条目的任何特定的迭代顺序
代码示例:
public static void main(String[] args) {
Map map=new HashMap();
map.put(9,"one");
map.put(2,"two");
map.put(6,"three");
map.put(4,"four");
System.out.println(map);
System.out.println("判断集合中是否包含键为2的映射: "+map.containsKey(2));
System.out.println("判断集合中国是否包含值为three: "+map.containsValue("three"));
Set set=map.keySet();
System.out.println(set);
Collection collection=map.values();
System.out.println(collection);
}
运行结果:
LinkedHashMap是Map接口的另一个实现类,继承自HashMap, 它使用双向链表在Map中存储条目,并保持迭代排序作为插入顺序
代码示例:
public static void main(String[] args) {
Map map=new LinkedHashMap();
map.put(9,"one");
map.put(2,"two");
map.put(6,"three");
map.put(4,"four");
System.out.println(map);
System.out.println("判断集合中是否包含键为2的映射: "+map.containsKey(2));
System.out.println("判断集合中国是否包含值为three: "+map.containsValue("three"));
Set set=map.keySet();
System.out.println(set);
Collection collection=map.values();
System.out.println(collection);
}
TreeMap是基于一个红黑树的实现,具体继承关系如下,它会按照你对Comparable接口或Comparator比较器的实现来对集合中的条目进行排序,同TreeSet相似,如果你设置了Comparator比较器,Comparable接口的功能将会被忽略。
下面的代码使用Comparator比较器对TreeMap中的条目进行排序:
public static void main(String[] args) {
//先比较键的长度,再忽略大小比较
Comparator keyComparator = Comparator.comparing(String::length).thenComparing(String::compareToIgnoreCase);
Map map=new TreeMap(keyComparator);
map.put("one",3);
map.put("two",4);
map.put("three",2);
map.put("four",1);
System.out.println(map);
}
运行结果:
HashTable直接实现Map接口,它与HashMap的区别如下:
详见我的另一博文:https://blog.csdn.net/qq_42013035/article/details/103452711