集合的概述
集合的由来
在介绍集合之前,应先了解java中对于不同的数据类型应该用什么来记录。
当需要在Java程序中记录单个数据内容时,则声明一个变量。
当需要在Java程序中记录多个类型相同的数据内容时,声明一个一维数组。
当需要在Java程序中记录多个类型不同的数据内容时,则创建一个对象。
当需要在Java程序中记录多个类型相同的对象数据时,创建一个对象数组。
当需要在Java程序中记录多个类型不同的对象数据时,则准备一个集合。
集合的框架结构**
Java中集合框架顶层框架是:java.util.Collection集合 和 java.util.Map集合。
其中Collection集合中存取元素的基本单位是:单个元素。
其中Map集合中存取元素的基本单位是:单对元素。
Collection集合
概念
Collection接口它是List接口、Queue 接口以及Set接口的父接口,因此该接口里定义的方法既可用于操作List集合,也可用于操作Queue集合和Set集合。
注意:Collections类是一个工具类,该类主要提供操作集合的一些方法。
Collection接口中常用的方法:
方法声明 | 功能介绍 |
---|---|
boolean add(E e) | 向集合中添加对象(单个元素); |
boolean addAll(Collection<? extends E>c) | 用于将参数指定集合c中的所有元素添加到当前集合中(一个一个添加); |
boolean contains(Object o) | 判断是否包含指定对象; |
boolean containsAll(Collection<?> c) | 判断是否包含参数指定的所有对象; |
boolean retainAll(Collection<?> c) | 保留当前集合中存在且参数集合中存在的所有对象; |
boolean remove(Object o) | 从集合中删除对象(删除整体对象); |
boolean removeAll(Collection<?> c) | 从集合中删除参数指定的所有对象(一个一个元素删); |
void clear() | 清空集合; |
int size() | 返回包含对象的个数; |
boolean isEmpty() | 判断是否为空; |
boolean equals(Object o) | 判断是否相等; |
int hashCode() | 获取当前集合的哈希码值; |
Object[] toArray() | 将集合转换为数组; |
Iterator iterator() | 获取当前集合的迭代器; |
Iterator接口(迭代器)
基本概念
-- java.util.Iterator接口主要用于描述迭代器对象,可以遍历Collection集合中的所有元素。
-- java.util.Collection接口继承Iterator接口,因此所有实现Collection接口的实现类都可以使用该迭代器对象。
遍历集合的方式
public class CollectionTest {
public static void main(String[] args) {
// 准备一个Collection集合并放入元素后打印
Collection c1 = new ArrayList();
c1.add("one");
c1.add(2);
c1.add(new Person("zhangsan", 30));//要先封装一个Person类
// 遍历方式一: 自动调用toString方法 String类型的整体
System.out.println("c1 = " + c1); // [one, 2, Person{name='zhangsan', age=30}]
System.out.println("------------------------------------------------");
// 遍历方式二:使用迭代器来遍历集合中的所有元素 更加灵活
// 获取当前集合中的迭代器对象
Iterator iterator1 = c1.iterator();
// 判断是否有元素可以访问
while (iterator1.hasNext()) {
// 取出一个元素并指向下一个
System.out.println("获取到的元素是:" + iterator1.next());
}
/**
遍历方式三: 使用for each结构实现集合和数组中元素的遍历 代码简单且方法灵活
格式:for(元素类型 变量名 : 数组/集合名称) {
循环体;
}
该方式其实是迭代器的简化版
*/
for (Object obj : c1) {
System.out.println("取出来的元素是:" + obj);
}
}
}
那如何在遍历的过程中进行元素的删除呢?
-- 可以用到迭代器提供的remove()方法
// 借助上面代码中的集合c1来实现
// 3.不断地去获取集合中的元素并判断,当元素值为"one"时则删除该元素
iterator1 = c1.iterator(); //重置迭代器
while (iterator1.hasNext()) {
Object obj = iterator1.next();
if("one".equals(obj)) {
iterator1.remove(); //使用迭代器的remove方法删除元素
ConcurrentModificationException并发修改异常
}
}
System.out.println("删除后集合中的元素有:" + c1); // [2, Person{name='zhangsan', age=30}]
List集合
java.util.List集合是Collection集合的子集合,该集合中允许有重复的元素并且有先后放入次序。
该集合的主要实现类有:ArrayList类、LinkedList类、Stack类、Vector类。
其中ArrayList类的底层是采用动态数组进行数据管理的,支持下标访问,增删元素不方便。
其中LinkedList类的底层是采用双向链表进行数据管理的,访问不方便,增删元素方便。可以认为ArrayList和LinkedList的方法在逻辑上完全一样,只是在性能上有一定的差别
ArrayList 更适合于访问而LinkedList更适合于插入和删除;在性能要求不是特别苛刻的情 形下可以忽略这个差别。
其中Stack类的底层是采用动态数组进行数据管理的,该类主要用于描述一种具有后进先 出特征的数据结构,叫做栈(last in first out LIFO)。
其中Vector类的底层是采用动态数组进行数据管理的,该类与ArrayList类相比属于线程安全的类,效率比较低,以后开发中基本不用。
常用的方法:
方法声明 | 功能介绍 |
---|---|
void add(int index, E element) | 向集合中指定位置添加元素 |
boolean addAll(int index, Collection<? extends E> c) | 向集合中添加所有元素 |
E get(int index) | 从集合中获取指定位置元素 |
int indexOf(Object o) | 查找参数指定的对象 |
int lastIndexOf(Object o) | 反向查找参数指定的对象 |
E set(int index, E element) | 修改指定位置的元素 |
E remove(int index) | 删除指定位置的元素 |
List subList(int fromIndex, int toIndex) | 用于获取子List(子集合和当前集合共用同一块内存空间) |
注意:使用工具类Arrays.asList()可以将数组转换成集合,返回的对象是一个Arrays内部类,使用该方法时注意不能使用修改集合的相关方法否则会抛出异常。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据依然是数组。
Queue集合
java.util.Queue集合是Collection集合的子集合,与List集合属于平级关系。
- 该集合的主要用于描述具有先进先出特征的数据结构,叫做队列(first in first out FIFO)。
- 该集合的主要实现类是LinkedList类,因为该类在增删方面比较有优势。
常用的方法:
方法声明 | 功能介绍 |
---|---|
boolean offer(E e) | 将一个对象添加至队尾,若添加成功则返回true |
E poll() | 从队首删除并返回一个元素 |
E peek() | 返回队首的元素(但并不删除) |
Set集合
java.util.Set集合是Collection集合的子集合,与List集合平级。
该集合中元素没有先后放入次序,且不允许重复。
该集合的主要实现类是:HashSet类 和TreeSet类以及LinkedHashSet类。
其中HashSet类的底层是采用哈希表进行数据管理的。
其中TreeSet类的底层是采用红黑树进行数据管理的。
其中LinkedHashSet类与HashSet类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
注意:该集合的常用方法参考Collection集合。
元素放入HashSet集合的原理:
使用元素调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算出该元素在数组中的索引位置。
若该位置没有元素,则将该元素直接放入即可。
若该位置有元素,则使用新元素与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
若新元素与已有元素的哈希值相同,则使用新元素调用equals方法与已有元素依次比较。
若相等则添加元素失败,否则将元素直接放入即可。
-- 那么问题来了,为什么要求重写equals方法后要重写hashCode方法呢?
答:当两个元素调用equals方法相等时证明这两个元素相同,重写hashCode方法后保证这两个元 素得到的哈希码值相同,由同一个哈希算法生成的索引位置相同,此时只需要与该索引位置已有元素比较即可,从而提高效率并避免重复元素的出现。
TreeSet集合
二叉树主要指每个节点最多只有两个子节点的树形结构。
满足以下3个特征的二叉树叫做有序二叉树:
a.左子树中的任意节点元素都小于根节点元素值;
b.右子树中的任意节点元素都大于根节点元素值;
c.左子树和右子树的内部也遵守上述规则;
由于TreeSet集合的底层采用红黑树进行数据的管理,当有新元素插入到TreeSet集合时,需要使用新元素与集合中已有的元素依次比较来确定新元素的合理位置。
比较元素大小的规则有两种方式:
使用元素的自然排序规则进行比较并排序,让元素类型实现java.lang.Comparable接口;
使用比较器规则进行比较并排序,构造TreeSet集合时传入java.util.Comparator接口;
自然排序的规则比较单一,而比较器的规则比较多元化,而且比较器优先于自然排序;
Map集合
概念
-- java.util.Map<K,V>集合中存取元素的基本单位是:单对元素,其中类型参数如下:
- K - 此映射所维护的键(Key)的类型,相当于目录。
- V - 映射值(Value)的类型,相当于内容。
该集合中key是不允许重复的,而且一个key只能对应一个value。
该集合的主要实现类有:HashMap类、TreeMap类、LinkedHashMap类、Hashtable类、Properties类。
其中HashMap类的底层是采用哈希表进行数据管理的。
其中TreeMap类的底层是采用红黑树进行数据管理的。
其中LinkedHashMap类与HashMap类的不同之处在于内部维护了一个双向链表,链表中记录了元素的迭代顺序,也就是元素插入集合中的先后顺序,因此便于迭代。
其中Hashtable类是古老的Map实现类,与HashMap类相比属于线程安全的类,且不允许null作为key或者value的数值。
其中Properties类是Hashtable类的子类,该对象用于处理属性文件,key和value都是String类型的。
Map集合是面向查询优化的数据结构, 在大数据量情况下有着优良的查询性能。
经常用于根据key检索value的业务场景。
常用的方法:
方法声明 | 功能介绍 |
---|---|
V put(K key, V value) | 将Key-Value对存入Map,若集合中已经包含该Key,则替换该Key所对应的Value,返回值为该Key原来所对应的Value,若没有则返回null; |
V get(Object key) | 返回与参数Key所对应的Value对象,如果不存在则返回null |
boolean containsKey (Object key) | 判断集合中是否包含指定的Key |
boolean containsValue (Object value) | 判断集合中是否包含指定的Value |
V remove(Object key) | 根据参数指定的key进行删除 |
Set keySet() | 返回此映射中包含的键的Set视图 |
Collection values() | 返回此映射中包含的值的Set视图 |
Set<Map.Entry<K,V>>entrySet() | 返回此映射中包含的映射的Set视图 |
这里就演示添加和遍历的操作:
// 1.准备一个Map集合
Map<String, String> m1 = new HashMap<>();
// 2.向集合中添加元素并打印
String str1 = m1.put("1", "one");
System.out.println("原来的value数值为:" + str1); // null
System.out.println("m1 = " + m1); // {1=one}
str1 = m1.put("2", "two");
System.out.println("原来的value数值为:" + str1); // null
System.out.println("m1 = " + m1); // {1=one, 2=two}
str1 = m1.put("3", "three");
System.out.println("原来的value数值为:" + str1); // null
System.out.println("m1 = " + m1); // {1=one, 2=two, 3=three}
// 实现了修改的功能
str1 = m1.put("1", "eleven");
System.out.println("原来的value数值为:" + str1); // one
System.out.println("m1 = " + m1); // {1=eleven, 2=two, 3=three}
/**
Map集合的三种遍历方式
*/
// 方式一:获取Map集合中所有的key并组成Set视图
Set<String> s1 = m1.keySet();
// 遍历所有的key
for (String ts : s1) {
System.out.println(ts + "=" + m1.get(ts));
}
// 方式二:获取Map集合中所有的Value并组成Collection视图
Collection<String> co = m1.values();
for (String ts : co) {
System.out.println("ts = " + ts);
}
// 方式三:获取Map集合中所有的键值对并组成Set视图
Set<Map.Entry<String, String>> entries = m1.entrySet();
for (Map.Entry<String, String> me : entries) {
System.out.println(me);
}
元素放入HashMap集合的原理:
使用元素的key调用hashCode方法获取对应的哈希码值,再由某种哈希算法计算在数组中的索引位置。
若该位置没有元素,则将该键值对直接放入即可。
若该位置有元素,则使用key与已有元素依次比较哈希值,若哈希值不相同,则将该元素直接放入。
若key与已有元素的哈希值相同,则使用key调用equals方法与已有元素依次比较。
若相等则将对应的value修改,否则将键值对直接放入即可。
相关的常量:
DEFAULT_INITIAL_CAPACITY : HashMap的默认容量是:16。
DEFAULT_LOAD_FACTOR:HashMap的默认加载因子是:0.75。
threshold:扩容的临界值,该数值为:容量*填充因子,也就是:12。
TREEIFY_THRESHOLD:若Bucket中链表长度大于该默认值则转化为红黑树存储,该数值是:8。
MIN_TREEIFY_CAPACITY:桶中的Node被树化时最小的hash表容量,该数值是:64。
Collections工具类类
概念
-- java.util.Collections类主要提供了对集合操作或者返回集合的静态方法。
注意:Collection是List和Set的父接口,而Collections是一个工具类。
常用的方法
方法声明 | 功能介绍 |
---|---|
static <T extends Object & Comparable<? super T>> T max(Collection<? extends T> coll) | 根据元素的自然顺序返回给定集合的最大元素 |
static T max(Collection<? extends T> coll, Comparator<?super T> comp) | 根据指定比较器引发的顺序返回给定集合的最大元素 |
static <T extends Object & Comparable<?super T>> T min(Collection<? extends T> coll) | 根据元素的自然顺序返回给定集合的最小元素 |
static T min(Collection<? extends T> coll, Comparator<?super T> comp) | 根据指定比较器引发的顺序返回给定集合的最小元素 |
static void copy(List<? super T> dest, List<? extends T>src) 将一个列表中的所有元素复制到另一个列表中 | |
static void reverse(List<?> list) | 反转指定列表中元素的顺序 |
static void shuffle(List<?> list) | 使用默认的随机源随机置换指定的列表 |
static <T extends Comparable<? super T>> void sort(List list) | 根据其元素的自然顺序将指定列表按升序排序 |
static void sort(List list, Comparator<? super T> c) | 根据指定比较器指定的顺序对指定列表进行排序 |
static void swap(List<?> list, int i, int j) | 交换指定列表中指定位置的元素 |