一、集合概念
集合–>存储容器
引入
举个例子,创建一个学生表,底层用数组来实现,当需要删除某一个学生数据时,仅仅是通过将所要删除的数组元素赋值为null,实际上数组长度没变,当需要添加新的数据时,又要考虑数组容量问题,毕竟数组一经声明,容量就是固定的,不太灵活。
然而在实际中,我们经常需要保存一些变长的数据集合,这时就需要能够动态增长长度的容器来存储数据。
为了满足程序运行中各种变化的数据存储需求,java语言中,封装了许多的了类,
来帮助我们完成不同数据的存储,这些类就被称为集合类.
核心思想就是我们要学习一些类,这些类对数组等数据结构进行了封装,可以让我们更加方便的使用.
二、集合 API
(一)集合体系概述
Java的集合框架是由很多接口、抽象类、具体类组成的,都位于java.util包中
4个接口 7个实现类
单列集合
Collection
List 可以存储重复元素
ArrayList 数据列表
LinkedList 链表列表
Vector 线程安全的数组列表
Set
HashSet 哈希集合
TreeSet 树形集合
双列集合
Map
键 : 值
HashMap
TreeMap
java中提供不同结构的容器类,来存储不同的数据
(二)Collection接口
Collection 接口-定义了存取一组对象的方法,其子接口Set和List分别定义
了存储方式。
List:数据元素可以重复,有顺序(添加顺序),还可以通过索引操作
Set:不能重复,添加时会自动判断,没有顺序(有的有序,有的无序)
三、List 接口及实现类
List继承了Collection接口,有三个实现的类
ArrayList 数据列表
LinkedList 链表列表
Vector 数据列表(线程安全)
List中方法名相同,但具体底层实现完全不同
(一)ArrayList: 数组列表
由于数组中的每个空间都是连续的,所以查询非常方便,可以直接通过下标获取指定位置的元素,但是从中间要删除元素,就比较麻烦,删除后其他元素要进行位移.
查询快,中间删除添加慢
ArrayList可以动态的增加数组长度
Ep1:
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListDemo1 {
/*
ArrayList
底层有一个transient Object[] elementData; 数组
默认是向集合的末尾添加元素,首次底层会初始化一个长度为10的数组,当元素添加满了的时候,会发生扩容操作,扩容为原来的1.5倍
ArrayList 默认可以存储任意类型的数据
ArrayList<E> 建议使用泛型的语法,为集合中存储的数据设定数据类型,
java中的集合,建议只存储一类类型
*/
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList<String>();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("e");
alist.add("f");
alist.add("g");
alist.add("h");
alist.add("i");
alist.add("j");
alist.add("k");
alist.add("l");
alist.add(0, "A");//向指定的位置添加
alist.remove(1);//删除指定位置的元素
alist.remove("h");//删除指定内容的元素
System.out.println(alist.size());//获取集合中实际有多少个元素
System.out.println(alist.get(4));//获取指定位置上的元素
System.out.println(alist);
alist.clear();//将数组列表清空
System.out.println(alist);
}
}
/*
11
e
[A, b, c, d, e, f, g, i, j, k, l]
[]
*/
Ep2:
import java.util.ArrayList;
import java.util.Arrays;
public class ArrayListDemo2 {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList<String>();//默认容量是10
alist.add("a");
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("c");
System.out.println(alist.contains("d"));//判断是否包含指定的元素
System.out.println(alist.isEmpty());//判断集合是否为空
//alist.ensureCapacity(20); 扩容为指定容量
System.out.println(alist.indexOf("c"));//获取指定元素首次出现的位置 从前向后找
System.out.println(alist.lastIndexOf("c"));//获取指定元素首次出现的位置 从后向前找
alist.set(0, "A");//将指定位置上的元素替换为给定元素
Object[] objs = alist.toArray();//将集合转为Object类型
System.out.println(objs.length);
String [] arrays = alist.toArray(new String[alist.size()]);//将集合转为一个String类型的数组
System.out.println(Arrays.toString(arrays));
System.out.println(alist);
}
}
/*
true
false
2
4
5
[A, b, c, d, c]
[A, b, c, d, c]
*/
(二)LikedList: 链表列表
每一个数据存储在一个Node类中 Node节点
Node
prev(前驱)
data(数据)
next(后继)
链表结构,对外只提供头,尾节点,每次查询元素时,非常慢,必须从头或者从尾开始
如果从中间删除某个元素时非常快,只需要改变next节点的内存地址即可(即将前一个next节点指向后一个prev节点),元素不需要移动位置
查询慢,但是中间添加删除快
Ep:
import java.util.LinkedList;
public class LinkedListDemo {
public static void main(String[] args) {
LinkedList<String> llist = new LinkedList<>();
llist.add("a");
llist.add("b");
llist.add("c");
llist.add("d");
llist.add("e");
llist.add("f");
llist.add(1, "AA");
System.out.println(llist);
System.out.println(llist.get(3));//返回指定索引位上的元素
llist.remove(4);//删除指定索引位上的元素
System.out.println(llist);
llist.remove("c");//删除指定元素
System.out.println(llist);
System.out.println(llist.size());
//LinkedList可以用来实现队列和栈结构容器
llist.addFirst("A");//给首位添加元素
System.out.println(llist);
llist.addLast("N");
System.out.println(llist);
System.out.println(llist.getFirst());//返回首位元素
System.out.println(llist.getLast());
System.out.println(llist);
System.out.println(llist.removeFirst());//返回并删除首位元素
System.out.println(llist.removeLast());
System.out.println(llist);
}
}
/*
[a, AA, b, c, d, e, f]
c
[a, AA, b, c, e, f]
[a, AA, b, e, f]
5
[A, a, AA, b, e, f]
[A, a, AA, b, e, f, N]
A
N
[A, a, AA, b, e, f, N]
A
N
[a, AA, b, e, f]
*/
(三)Vector
与ArrayList基本类似
List Vector 数组列表 里面的方法,都添加 synchronized关键字,是线程安全的
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
四、List接口集合迭代
(一)for循环遍历
import java.util.ArrayList;
public class ListFor {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("e");
//for循环进行遍历
for(int i=0;i<alist.size();i++){
String item = alist.get(i);
System.out.println(item);
}
}
}
/*
a
b
c
d
e
*/
import java.util.ArrayList;
public class ListFor {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("e");
/*
for循环进行遍历
在遍历元素时,是可以删除元素的,删除元素时,需要注意元素的移动,以及索引的变化
*/
for(int i=0;i<alist.size();i++){
String item = alist.get(i);
if(item.equals("c")){
alist.remove(item);
i--;
}
}
System.out.println(alist);
}
}
/*
[a, b, d, e]
*/
(二)增强for循环的遍历
import java.util.ArrayList;
public class ListFor {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("e");
//增强for循环
for(String item : alist){
System.out.println(item);
}
}
}
/*
a
b
c
d
e
*/
import java.util.ArrayList;
public class ListFor {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("d");
alist.add("e");
/*
增强for循环
在遍历元素时,不允许从集合中删除元素,如果删除元素报异常ConcurrentModificationException
*/
for(String item : alist){
if(item.equals("c")){
alist.remove(item);
}
}
System.out.println(alist);
}
}
/*
[a, b, d, e]
*/
(三)迭代器遍历(Iterator)
java中提供了对集合进行遍历的迭代器
里面维护了一个指针(计数器),当删除元素时,计数器会自动回退
import java.util.ArrayList;
import java.util.ListIterator;
public class listiteratorDemo {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("c");
alist.add("d");
alist.add("e");
/*
java中提供了对集合进行遍历的迭代器
里面维护了一个指针(计数器),当删除元素时,计数器会自动回退
*/
//获取集合的迭代器
/*ListIterator<String> lit = alist.listIterator();
while(lit.hasNext()){
String item = lit.next();
System.out.println(item);
}*/
// 从后向前进行遍历 给定开始的位置
ListIterator<String> list = alist.listIterator(alist.size()); //0-5 6
//从后向前进行遍历
while(list.hasPrevious()){
String item = list.previous();//取出前一个
System.out.println(item);
}
}
}
import java.util.ArrayList;
import java.util.Iterator;
public class iteratorDemo {
public static void main(String[] args) {
ArrayList<String> alist = new ArrayList();
alist.add("a");//向末尾添加
alist.add("b");
alist.add("c");
alist.add("c");
alist.add("d");
alist.add("e");
Iterator<String> it = alist.iterator();
//判断有没有下一个元素
while(it.hasNext()){
String item = it.next();//取出元素
if(item.equals("c")){
it.remove();//使用迭代器提供的方法进行删除元素
}
}
System.out.println(alist);
}
}
五、Set 接口
不可以存储重复元素,添加时,会自定的判断,有的是无序,有的是有序的.
(一)HashSet
是无序的,既不按照添加顺序存放,也不按照内容自然顺序的存放
是调用hashCode()方法计算hash值,为了更快速的判断
但是只用hash判断,不安全,
要对hashCode方法进行重写
当hash值相同时,再调用equals()方法判断内容是否相同,保证了安全
import java.util.HashSet;
public class HashDemo2 {
public static void main(String[] args) {
/*
HashSet添加元素时,是如何去除重复元素的.
判断元素是否重复时,没有直接使用equals()方法进行判断,因为equals()效率比较低.
java中在比较是否重复时,先是调用hashCode(),计算出内容的hash值(整数),
两个整数判断是否相等,是比较简单且快的,但是不同内容计算出来的hash值有可能相同,
所以当hash值出现相同时,再调用equals()方法进行判断,
这样既安全,又快速.
*/
HashSet<String> hset = new HashSet<>();
hset.add("a");
hset.add("a");
hset.add("通话");//1179410
hset.add("重地");//1179410
System.out.println(hset);
}
}
(二)TreeSet
可以给Set集合中的元素进行指定方式的排序。存储的对象必须实现Comparable接口。
TreeSet底层数据结构是二叉树(红黑树是一种自平衡的二叉树)
添加元素时,可以比较元素的大小, comparTo() 小于0 等于0 大于0
import java.util.TreeSet;
public class TreesetDemo1 {
public static void main(String[] args) {
/*
Set
TreeSet 不能存储重复元素
以按照值的自然顺序排序
*/
TreeSet<String> tset = new TreeSet();
tset.add("c");
tset.add("b");
tset.add("a");
tset.add("d");
tset.add("c");
System.out.println(tset);
}
}
import java.util.TreeSet;
public class TreesetDemo2 {
public static void main(String[] args) {
//向TreeSet中添加的元素的类,必须要实现Comparable接口,支持比较大小进行排序
Car car1 = new Car(101, "宝马1");
Car car2 = new Car(102, "宝马2");
Car car3 = new Car(103, "宝马3");
Car car4 = new Car(105, "宝马1");
Car car5 = new Car(104, "宝马5");
TreeSet<Car> tset = new TreeSet();
tset.add(car1);
tset.add(car2);
tset.add(car3);
tset.add(car4);
tset.add(car5);
System.out.println(tset);
}
}
六、Set 接口集合迭代
遍历方式: 因为set集合,元素没有索引,索引就不能使用普通的for
增强for循环
迭代器遍历 iterator();
与List迭代器类似
七、Map 接口
Map:
双列数据存储(键值对存储)
键–值
键值映射
键不能重复 值可以重复
每个键最多只能映射到一个值
(一)HashMap
HashMap中元素的key值不能重复,排列顺序是不固定的,可以存储一个为null的键.
import java.util.Collection;
import java.util.HashMap;
import java.util.Set;
public class HashMapDemo1 {
public static void main(String[] args) {
/*
Map 键值对形式存储数据
键不能重复,值可以重复
HashMap
键是无序的,只能存储一个为null的键
*/
HashMap<String,String> map = new HashMap();
map.put("a","aa");
map.put("z","ss");
map.put("s","ss");
map.put("h","hh");
map.put("a","aaa");
//map.clear();清空map中所有的键值
//map.remove("a"); 删除指定的key
System.out.println(map.get("h"));//通过key获取值
System.out.println(map.containsKey("a"));//判断是否包含指定的key
System.out.println(map.containsValue("ss"));//判断是否包含指定的value
System.out.println(map.isEmpty());//判断是否为为空
System.out.println(map.size());//键值对的数量
Collection<String> list = map.values();//获取值
System.out.println(list);
Set<String> keyset = map.keySet();//获取map中所有的key
for(String key : keyset){
String val = map.get(key);
System.out.println(key+"::"+val);
}
System.out.println(map);
}
}
/*
hh
true
true
false
4
[aaa, ss, hh, ss]
a::aaa
s::ss
h::hh
z::ss
{a=aaa, s=ss, h=hh, z=ss}
*/
(二)TreeMap
TreeMap中所有的元素都保持着某种固定的顺序,如果需要得到一个有序的Map就应该使用TreeMap,key值所在类必须实现Comparable接口.
(三)Hashtable
键值对存储
不能存储为null的key
Hashtable底层结构与HashMap类似
Hashtable是线程安全
(四)HashMap的底层结构
底层首先是数组:默认长度是16
比如要存储数据a—>ascll码97
进行一种运算(a%16,a&15)值(元素在数组中的位置,要保证在数组范围内)
首先底层是一个数组,用来确定元素在数组中的位置,数组长度只要使用率达到75%就会进行扩容(扩到原来的2倍),不会占满数组,否则查询效率会比较慢。
当存储一个元素时,首先会根据数据内容计算hash值,再用公式计算出在数组中的位置,然后在这个数组位置里再放入一个链表或者红黑树,链表长度等于8时转为红黑树。
(五)Map集合遍历
1.根据键找值
获取所有键的集合
遍历键的集合,获取到每一个键
根据键找值
2.根据键值对对象找键和值
获取所有键值对对象的集合
遍历键值对对象的集合,获取到每一个键值对对象
根据键值对对象找键和值
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapFor {
public static void main(String[] args) {
HashMap<String,String> map = new HashMap<>();
map.put("a","a");
map.put("b","b");
map.put("z","ss");
map.put("s","ss");
map.put("h","hh");
map.put("q","aaa");
/*
方式1: keySet(); 获取map中 所有的key的集合
变向的遍历map
*/
Set<String> keyset = map.keySet();
for(String key : keyset){
System.out.println(map.get(key));
}
/*
方式2 : 在遍历时,将key-value元素,
封装到一个个Entry对象中,
Entry对象中包含了key和value
*/
Set<Map.Entry<String,String>> entrySet = map.entrySet();
for(Map.Entry<String,String> entry : entrySet){
System.out.println(entry.getKey()+"::"+entry.getValue());
}
}
}
/*
a
aaa
b
ss
hh
ss
========方法一========方法二========
a::a
q::aaa
b::b
s::ss
h::hh
z::ss
*/
八、Collections类
Collection 接口 是单列集合的顶级接口
Collections类 提供了一些关于集合操作的方法,类似于Arrays类
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class Demo {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("a");
list.add("c");
list.add("d");
list.add("b");
System.out.println(list);//[a, c, d, b]
Collections.addAll(list, "b","c","d");//在原来的列表的末尾添加新的元素
System.out.println(list);//[a, c, d, b, b, c, d]
System.out.println(Collections.binarySearch(list, "b")); //二分查找
Collections.sort(list);//排序
System.out.println(list);//[a, c, d, b, b, c, d]
Collections.swap(list, 0, 3);//交换指定位置上的元素
System.out.println(list);//[c, b, b, a, c, d, d]
ArrayList<String> dest = new ArrayList<>();
dest.add("1");
dest.add("2");
dest.add("3");
dest.add("4");
dest.add("5");
dest.add("6");
dest.add("7");
dest.add("8");
Collections.copy(dest, list);//把源数组复制到目标数组中,目标数组的size()大于等于源数组size(),源数组会覆盖新数组的对应位置的内容
System.out.println(list);
System.out.println(dest);
List emptyList = Collections.emptyList();//返回一个空集合(内部类),不能使用,为了避免出现空指针
//emptyList.add("1");//报错,emptyList不能使用,只用来返回一个空集合
System.out.println(emptyList);//[]
//Collections.fill(list,"+");//用指定内容填充集合 会覆盖新数组的对应位置的内容
//System.out.println(list);[+,+,+,+,+,+,+]
Collections.reverse(list);//逆序
System.out.println(list);//[d, d, c, a, b, b, c]
Collections.shuffle(list);//随机调整元素的位置
System.out.println(list);
System.out.println(Collections.max(list));//返回最大的元素d
//int...a可变长度参数 本质是一个数组 在一个参数列表中,只能有一个可变长度的参数,并且只能放在参数列表的最后面
Demo.test(1,2,3);
}
public static void test(int b,int...a){
System.out.println(Arrays.toString(a));
}
}