一、Collection类
集合:Java中提供的一种容器,可以用来存储多个引用类型的数据。
集合与数组的区别:
- 数组的长度是固定的,而集合的长度是可变的。
- 集合存储的都是引用数据类型,若想存储基本数据类型需要存储对应的包装类。
1. 概述
1.1 简介
Collection:单列集合类的根接口,用于存储一些列符合某种规则的元素,其有两个常用的子接口:
-
java.util.List
:元素存取有序、有索引、且可重复,主要的实现类:java.util.ArrayList
查询快、增删慢java.util.LinkedList
查询慢、增删快 -
java.util.Set:
元素无索引、且不可重复,主要实现类:java.util.HashSet
存取无序java.util.LinkedHashSet
存取有序java.util.TreeSet
可以对集合中的元素进行排序
1.2 常用方法
Collection是所有单列集合的父接口,因此在Collection中定义了一些单列集合通用的方法,这些方法可用于操作所有单列集合。
public boolean add(E e); 把给定的对象添加到当前集合中 。
public void clear(); 清空集合中所有的元素。
public boolean remove(E e); 把给定的对象在当前集合中删除。
public boolean contains(Object obj); 判断当前集合中是否包含给定的对象。
public boolean isEmpty(); 判断当前集合是否为空。
public int size(); 返回集合中元素的个数。
public Object[] toArray(); 把集合中的元素,存储到数组中
2. 迭代器
2.1 使用
迭代:即Collection
集合元素的通用获取方式。在取元素之前先要判断集合中有没有元素,如果有,就把这个元素取出来,继续再判断,如果还有就再取出来。一直把集合中的所有元素全部取出。这种取出方式专业术语称为迭代。
常用方法
public Iterator iterator(); //获取迭代器对象
public E next(); //返回迭代的下一个元素
public boolean hasNext() //如果仍有元素可以迭代,则返回 true。
使用方式
当遍历集合时,首先通过调用t集合的iterator()
方法获得迭代器对象,然后使用hashNext()
方法判断集合中是否存在下一个元素,如果存在,则调用next()
方法将元素取出,否则说明已到达了集合末尾,停止遍历元素。
常见问题
- 若集合中已无元素可迭代时,仍使用迭代器的
next()
方法,将会抛出java.util.NoSuchElementException
异常,若想重新迭代就需要重新获取一个新的迭代器。 - 在进行集合元素迭代时,若添加/移除集合中的元素,将导致无法继续迭代,抛出
ConcurrentModificationException
异常。
原理
iterator迭代器对象在遍历集合时,内部采用指针的方式来跟踪集合中的元素。
- 在调用
Iterator
的next()
方法之前,迭代器的索引位于第一个元素之前,不指向任何元素 - 当第一次调用迭代器的
next()
方法后,迭代器的索引会向后移动一位,指向第一个元素并将该元素返回,当再次调用next()
方法时,迭代器的索引会指向第二个元素并将该元素返回 - 依此类推,直到
hasNext()
方法返回false
,表示到达了集合的末尾,终止对元素的遍历。
2.2 增强for循环
增强for循环专门用来遍历数组和集合的。它的内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作,否则会抛出ConcurrentModificationException
异常。
public class JavaTest {
public static void main(String[] args) {
Collection<String> strings = new ArrayList<String>(); //构造集合
strings.add("abc"); //添加元素
strings.add("cba");
strings.add("nba");
strings.add("def");
for (String str:strings) { //高级for循环遍历集合元素
System.out.println(str);
}
Iterator<String> iterator = strings.iterator(); //创建迭代器对象
while (iterator.hasNext()) { //判断是否可以继续迭代
System.out.println(iterator.next()); //获取下一个元素
if (strings.contains("nba")) { //判断是否包含指定元素
strings.remove("nba"); //删除指定的元素
} //报java.util.ConcurrentModificationException异常
}
}
}
二、Collections类
java.utils.Collections
是集合工具类,其成员方法都是静态方法,用来对collection
进行操作。
1. 排序
1.1 常用方法
public static void shuffle(List<?> list); //打乱集合顺序。
public static <T> void sort(List<T> list); //将集合中元素按照默认规则排序。
public static <T> void sort(List<T> list,Comparator<?superT>);//将集合中元素按照指定规则排序。
1.2 默认规则排序
- 在
collection
元素的类需要继承Compareable
接口,并重写compareTo()
方法。this - 参数
为升序,反之则为降序 - 创建
Collection
集合并添加要排序类的元素 - 调用
Collections.sort()
方法即可对Collection
中的元素排序
class Cls implements Comparable<Cls>{ //实现Compareable接口
private int age;
public Cls(int age) {
this.age = age;
}
@Override
public int compareTo(Cls c) {
return this.age - c.age;
}
@Override
public String toString() {
return age + " ";
}
}
public class JavaTest {
public static void main(String[] args) {
Cls c1 = new Cls(10);
Cls c2 = new Cls(20);
Cls c3 = new Cls(30);
Cls c4 = new Cls(40);
ArrayList<Cls> cls = new ArrayList<>();
cls.add(c1);
cls.add(c3);
cls.add(c4);
cls.add(c2);
Collections.sort(cls);
System.out.println(cls); //[10 , 20 , 30 , 40 ]
}
}
1.3 自定义规则排序
直接调用 Collections.sort()
方法,传入要排序的集合和Comparator
的匿名内部类(内部重写了compare()
方法)
class Cls{
public int age;
public Cls(int age) {
this.age = age;
}
}
public class JavaTest {
public static void main(String[] args) {
Cls c1 = new Cls(10);
Cls c2 = new Cls(20);
Cls c3 = new Cls(30);
Cls c4 = new Cls(40);
ArrayList<Cls> cls = new ArrayList<>();
cls.add(c1);
cls.add(c3);
cls.add(c4);
cls.add(c2);
Collections.sort(cls, new Comparator<Cls>() { //匿名内部类
@Override
public int compare(Cls o1, Cls o2) {
return o2.getAge() - o1.getAge();
}
});
}
}
2. 可变参数
作用:在JDK1.5之后,如果定义一个方法需要接收多个同类型参数,可以使用可变参数进行简化,当传入多个参数时,相当于传入了一个数组。
- 一个方法只能有一个可变参数
- 如果方法中有多个参数,可变参数要放到最后。
class Cls{
public String name;
public void method(int age, String... name) { //可变参数
for (String str:name) {
System.out.println(str); //遍历传入的数据
}
}
}
public class JavaTest {
public static void main(String[] args) {
Cls cls = new Cls();
cls.method(10, "a", "b", "c");
//可以一次性向集合中添加多个,而不需要逐个添加
ArrayList<String> strings = new ArrayList<>();
Collections.addAll(strings, "a", "b", "c");
}
}
三、List接口
1. 概述
特点
java.util.List
接口继承自Collection
接口,是单列集合的一个重要分支,习惯性地会将实现了List
接口的对象称为List集合。元素特点是存取有序、带有索引、可重复。
常用方法
List
作为Collection
集合的子接口,不但继承了Collection
接口中的全部方法,还增加了一些独有的方法。
public void add(int index, E element); //将指定的元素,添加到该集合中的指定位置上。
public E get(int index); //返回集合中指定位置的元素。
public E remove(int index); //移除列表中指定位置的元素, 返回的是被移除的元素。
public E set(int index, E element); //用指定元素替换集合中指定位置的元素,返回值的更新前的元素。
2. ArrayList集合
java.util.ArrayList
集合数据存储的结构是数组结构。元素增删慢,查找快,由于日常开发中使用最多的功能为查询数据、遍历数据,所以ArrayList
是最常用的集合。
常用方法
其内部没有独特的方法,都是实现的Collection
接口和List
接口的方法
ArrayList(); //构造方法,构造一个初始容量为 10 的空列表。
ArrayList<E> list1 = new ArrayList<E>(); //构造方法,构造一个E类型的列表
public boolean add(E e); //想集合尾部插入数据
public void add(int index,E element); //向指定位置插入数据
public boolean remove(Object o); //删除指定的元素,返回删除是否成功
public E remove(int index); //删除指定索引处的元素,返回被删除的元素
public E set(int index, E element); //修改指定索引处的元素,返回被修改的元素
public E get(int index); //返回指定索引处的元素
public int size(); //返回集合中的元素的个数
3. LinkedList集合
java.util.LinkedList
集合数据存储的结构是双向链表。方便元素添加、删除的集合。
常用方法
实际开发中对一个集合元素的添加与删除经常涉及到首尾操作,而LinkedList提供了大量首尾操作的方法。
public void addFirst(E e); //将指定元素插入此列表的开头。
public void addLast(E e); //将指定元素添加到此列表的结尾。
public E getFirst(); //返回此列表的第一个元素。
public E getLast(); //返回此列表的最后一个元素。
public E removeFirst(); //移除并返回此列表的第一个元素。
public E removeLast(); //移除并返回此列表的最后一个元素。
public E pop(); //从此列表所表示的堆栈处弹出一个元素。
public void push(E e); //将元素推入此列表所表示的堆栈。
四、Set接口
1. 概述
Set接口:也称Set集合,但凡是实现了Set接口的类都叫做Set集合,特点是元素无索引、不可重复(唯一)
- Set集合没有特殊的方法,都是使用Collection接口的方法
- Set集合没有索引,所以遍历元素的方式就只有: 增强for循环,或者迭代器
2. HashSet集合
2.1 原理
底层数据结构
-
HashSet
集合存储数据的结构是哈希表- JDK1.8之前哈希表底层采用数组+链表实现,同一哈希值的链表都存储在一个数组中,但当前链表的元素过多时,通过key遍历查找效率较低
- JDK1.8及之后哈希表底层采用数组+链表+红黑树,当链表元素长度超过8时,链表会转为红黑树,提高了遍历效率。
-
HashSet
集合保证元素唯一时依赖于hashCode()
和equals()
方法- 当存储元素时
HashSet
会调用该元素的hashCode()
方法计算其哈希值 - 判断数组中该哈希值对应的位置上是否有元素,若没有则直接存储;
- 若有对应位置上有元素则说明产生了哈希冲突,会调用该元素的
equals()
方法与对应位置上的元素逐一比较 - 若该位置上有任意元素与该元素相同则不存储,若没有相同则在当前位置的链表/红黑树上添加一个新节点。
- 当存储元素时
源码
HashSet
底层是HashMap
public class HashSet<E> extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable{
private transient HashMap<E,Object> map;
//构造函数
public HashSet() { //实际上内部使用HashMap实现的
map = new HashMap<>();
}
}
2.2 存储自定义类
-
重写写该类的
hashCode()
和equals()
方法。由于
HashSet
保证元素唯一性是通过hashCode()
根据元素地址生成的哈希值和equals()
比较元素的地址,因此在存储自定义类的元素时,需要重写该类的hashCode()
和equals()
方法。public class Person{ public String name; public int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override //重写equals()方法 public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Person person = (Person) o; return age == person.age && Objects.equals(name, person.name); } @Override //重写hashCode()方法 public int hashCode() { return Objects.hash(name, age); } }
-
使用方法
//自定义类 class CA { private String name; private int age; public CA(String name, int age) { this.name = name; this.age = age; } @Override //重写equals方法 public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; CA ca = (CA) o; return age == ca.age && Objects.equals(name, ca.name); } @Override //重写hashCode方法 public int hashCode() { return Objects.hash(name, age); } @Override public String toString() { return name + ": " + age; } } public class JavaTest { public static void main(String[] args) { CA ca0 = new CA("abc", 10);a //定义自定义类元素 CA ca1 = new CA("123", 11); CA ca2 = new CA("abe", 12); CA ca3 = new CA("abc", 10); HashSet<CA> cas = new HashSet<>(); //创建HashSet集合 cas.add(ca0); //添加自定义类对象到HashSet集合 cas.add(ca1); cas.add(ca2); cas.add(ca3); for (CA ca: cas) { System.out.println(ca.toString()); //abc: 10 //重写了方法,只有一个该元素 } //abe: 12 } //123: 11 //存取无序 }
3. LinkedHashSet集合
**作用:**在HashSet
下面的子类java.util.LinkedHashSet
,它是链表和哈希表组合的一个数据存储结构,可以解决HashSet
存取无序。
public class Test {
public static void main(String[] args) {
LinkedHashSet<Integer> set = new LinkedHashSet<>();// 存取有序
// 往集合中存储数据
set.add(300);
set.add(100);
set.add(200);
set.add(500);
set.add(400);
set.add(400);
System.out.println(set);// [300, 100, 200, 500, 400]
}
}
4. TreeSet集合
TreeSet
集合是Set
接口的一个实现类,底层依赖于TreeMap
,是一种基于红黑树的实现,使用元素的自然新婚徐对元素进行排序,或者根据创建TreeSet
时提供的 Comparator
比较器进行排序,具体取决于使用的构造方法:
public TreeSet(); //根据其元素的自然排序进行排序
public TreeSet(Comparator<E> comparator); //根据指定的比较器进行排序
class Cls{
public int age;
}
public class JavaTest {
public static void main(String[] args) {
TreeSet<Cls> cls1 = new TreeSet<>(new Comparator<Cls>() {
@Override
public int compare(Cls o1, Cls o2) {
return o1.age - o2.age;
}
});
}
}
五、Map集合
1. 特点
Map<K,V>
集合的特点: K用来限制键的类型,V用来限制值的类型
Map
集合存储元素是以键值对的形式存储,每一个键值对都有键和值Map
集合的键是唯一,值可以重复,如果键重复了,那么值就会被覆盖
Map集合子类:
- HashMap<K,V>
:存储数据采用的哈希表结构,元素的存取无序。
由于要保证键的唯一、不重复,需要重写键的hashCode()
方法、equals()
方法。
LinkedHashMap<K,V>
:HashMap
下的子类,存储数据采用的哈希表结构+链表结构,元素存取有序。
通过哈希表结构可以保证的键的唯一、不重复,需要重写键的hashCode()
方法、equals()
方法。TreeMap<K,V>
:TreeMap
集合和Map
相比没有特有的功能,底层的数据结构是红黑树;
可以对元素的键进行排序,排序方式有两种:自然排序和比较器排序
具体的使用方法可以参考Set集合子类的方法
2. 常用方法
public V put(K key, V value); //添加键值对到Map集合中。
public V remove(Object key); //移除指定的键值对,返回被移除的值
public V get(Object key); //根据key,获取value
public boolean containsKey(Object key); //判断该集合中是否包含key
public Set<K> keySet(); //获取Map集合中所有的键,存储到Set集合中。
public Set<Map.Entry<K,V>> entrySet(); //获取到Map集合中所有的键值对对象的集合(Set集合)。
3. 遍历
遍历可以通过键找值的方式,也可以通过键值对的方式
public class JavaTest {
public static void main(String[] args) {
Map<String, Integer> stringIntegerMap = new HashMap<>();
stringIntegerMap.put("abc", 1);
stringIntegerMap.put("abd", 2);
stringIntegerMap.put("abe", 3);
stringIntegerMap.put("abf", 4);
//通过键找值
Set<String> strings = stringIntegerMap.keySet();
for (String string:strings) {
System.out.println(string + " " + stringIntegerMap.get(string));
}
//直接获取键值对
Set<Map.Entry<String, Integer>> entries = stringIntegerMap.entrySet();
for (Map.Entry<String, Integer> entrie : entries) {
System.out.println(entrie.getKey() +" "+ entrie.getValue());
}
}
}