Java中的集合
包含以下结构:
数组-线性表
链表
栈
队列
散列表
二叉树
映射关系(key-value)
List集合
特点:[有序、重复] 【线性表--数组】
ArrayList
定义
线程不安全的、物理地址上是连续的--有序的、可重复的、线性结构的(线性表);
导包 import java.util.ArrayList;
工作原理
底层是通过
数组来实现的,当存储的数据到达一定的个数的时候(默认为10),长度不够时,会扩充到原来的1.5倍 集合的容量;(eg:)
扩容步骤(可以通过
CTRL键点击以下函数进入底层理解函数):
add(Object obj)//添加数据
ensureCapacityInternal(size+1);
ensureExplicitCapacity(minCapacity);//赋值判断
grow(minCapacity);//扩容
注意:所有的集合中存放的数据都是
引用类型
,没有基本类;
基本方法
添加
List list = new ArrayList<>();
list.add(10);// Integer
list.add("abc");
list.add('a');
list.add(1.66);
list.add(1.66);
删除
1.根据索引删除数据:集合索引也是从0开始 超出范围会出现IndexOutOfBoundsException
list.remove(100);
2.根据对象进行删除
list.remove("abc");
// 注意:调用 int index的方法
list.remove(10);//删除索引为10的数据
list.remove(new Integer(10));//输出数据为10的数据,因为集合中保存的数据都是对象
获取数据
list.get(0);//根据索引获取数据
添加某个集合中的数据
List list1 = new ArrayList<>();
list1.add("abcd");
list1.add("aaaa");
list.addAll(list1);//添加集合list1中的所有数据
System.out.println(list);//打印结果
指定某个位置插入数据
list1.add("abcd");
list1.add("aaaa");
list1.add(2, 123);
System.out.println(list1);
替换
list.set(0, 110);//在位置为0,替换数据为110
获取集合大小
list.size();
根据对象获取对应索引
list.indexOf(
"abc");//获取对象abc 的索引
判断集合中是否有对象数据
list.contains("abc");//判断集合中是否有abc数据
遍历集合
1.foreach(本身是用了迭代器来进行遍历)
for (Object object : list1) {
System.out.println(object);}
2.for循环
for(int i=0;i<list.size();i++) {
System.out.println(list.get(i));}
迭代器
Iterator iterator = list.iterator();
//遍历获取
while(iterator.hasNext()) {//判断迭代器中是否有下一个数据
System.out.println(iterator.next());//获取数据
}
// 两次获取,分别取得list中的第一个和第二个数据
System.out.println(iterator.next());//获取数据
System.out.println(iterator.next());//获取数据
LinkedList
定义
链表结构的集合、物理地址上不是连续的,通过引用指针的形式进行关联【底层通过内部类Node来存储数据及前后节点的关联】;
导包
import java.util.LinkedList;
存放的数据
有序的、可重复的【链表结构,队列】
特别:LinkedList:即实现了Dueue列的接口,也实现了List的接口,
拥有了
队列
和
List的功能
方法
添加
LinkedList list = new LinkedList<>();
//添加--默认在末尾
list.add("abc");
list.add("a");
list.add("b");
list.add("c");
list.add(1);
list.add(1);
System.out.println(list);//输出结果
list.addFirst("first");//在头部
list.addLast("last");//在末尾
System.out.println(list);//输出结果
获取
list.get(0);//获取索引值为0,即第一位的数据--与ArrayList同
删除
LinkedList list = new LinkedList<>();
list.add("abc");
list.add("a");
list.removeFirst();//删除第一个数据
list.removeLast();//删除第二个数据
System.out.println(list);//打印结果
修改
list.set(index, element);
遍历
[普通的for、foreach、迭代器]
for (Object object : list) {
System.out.println(object);}
队列的操作
LinkedList list = new LinkedList<>();
list.push("first");//压进去
list.push("last");//压进去
System.out.println(list);
System.out.println(list.peek());//获取头部元素,但不移除
System.out.println(list);
System.out.println(list.pop());//获取头部元素,并且移除
LinkedList和ArrayList的区别
(1)性能:
批量添加数据时ArrayList>LinkedList
但是涉及到
索引的插入及修改操作时LinkedList>ArrayList
//LinkedList和ArrayList性能测试
public static void main(String[] args) {
// 批量添加数据时ArrayList>LinkedList 但是涉及到索引的插入及修改操作时LinkedList>ArrayList
// 对比,指定某个位置插入数据
//创建一个LinkedList
LinkedList linked = new LinkedList();
linked.add("a");
linked.add("b");
linked.add("c");
//创建一个ArrayList
ArrayList array = new ArrayList();
array.add("a");
array.add("b");
array.add("c");
//LinkedList插入100000数据
long start = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
linked.add(1,"abc");
}
long end = System.currentTimeMillis();
System.out.println("LinkedList的操作时长:" + (end - start));
//ArrayList插入100000数据
start = System.currentTimeMillis();
for (int i = 1; i <= 100000; i++) {
array.add(1,"abc");
}
end = System.currentTimeMillis();
System.out.println("ArrayList的操作时长:" + (end - start));}
(2)LinkedList有队列的功能,ArrayList
只有List的功能
vector
定义
Vector是线程安全的、有序的、可重复的。(常用于多线程)
(可以通过设置capacityIncrement【增长因子】来设定扩容的次数,一般默认为0,也是默认两倍的扩容)
方法
Vector和ArrayList的操作基本相似的, 但是Vector是线程安全的,ArrayList是线程不安全的
public static void main(String[] args) {
Vector vector = new Vector<>();
//
vector.addElement("abc");
vector.add("a");
vector.add("a");
vector.addElement("c");
vector.removeElementAt(0);
//
System.out.println(vector);
}
Stack(栈)
Vector中有一个子类Stack,是一个栈结构的集合,工作原理是
先进后出 后进先出(类似弹夹)
public static void main(String[] args) {
// 栈结构:先进后出,后进先出
Stack stack = new Stack<>();
//push 压栈
stack.push("a");
stack.push("b");
stack.push("c");
//
System.out.println(stack);
//出栈
System.out.println(stack.pop()); //获取并移除的操作
System.out.println(stack);
}
Set集合
HashSet
定义
散列表结构,【无序、不允许重复】 ,存储的引用类型的对象,
方法
public static void main(String[] args) {
Set set = new HashSet();
// 添加 [没有索引插入数据]
set.add("abcd");
set.add("a");
set.add(1);
set.add(16.66);
set.add("abc");
set.add("abc");
// 不能随机获取【根据索引获取】 没有set修改操作
System.out.println(set.contains(1));
// 删除数据 set.remove(o)
System.out.println(set);
}
存储原理
1、
获取对象的hashcode 根据hashcode计算存储的
位置【取余】
2、
遍历链表,使用equals方法
判断当前添加的对象和链表中的数据
是否重复【
不重复则加入、重复则不加入】
重写类型equals和hashcode方法
public class Person {
public String id;
public String name;
public double wealth;
public Person(String id, String name, double wealth) {
super();
this.id = id;
this.name = name;
this.wealth = wealth;}
public Person() {super();}
// 当Person中的三个属性值一致的时候,则主观上认为是一致的对象
@Override
public boolean equals(Object obj) {
// 重写equals:三个属性值一致
System.out.println("Person调用了equals方法");
// 1.判断对象
if (this == obj) {
return true;
}
// 2.判断hashCode
if (this.hashCode() != obj.hashCode()) {
return false;
}
// 强制类型转换
Person pobj = (Person) obj;
// 3.判断属性值
if (!this.id.equals(pobj.id)) {
return false;
}
if (!this.name.equals(pobj.name)) {
return false;
}
if (this.wealth != pobj.wealth) {
return false;
}
return true;
}
@Override
public int hashCode() {
// 定义hashcode的生成规则为属性值hashcode的和
return (int) (id.hashCode() + name.hashCode() + wealth);
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", wealth=" + wealth + "]";
}
}
//测试类
public static void main(String[] args) {
Person p1 = new Person("001", "zhang", 1000);
Person p2 = new Person("001", "zhang", 1000);
Person p3 = new Person("002", "li", 1000);
set.add(p1);
set.add(p2);
System.out.println("---------------------");
//当添加p3
set.add(p3);
System.out.println(p1.hashCode());
System.out.println(p2.hashCode());
System.out.println(set);
}
LinkedHash
定义
数据结构还是
散列表 ;只是通过
借助链表来
维护插入数据的顺序,但是存储位置依然是无序的 (不能通过下标访问)
;其他操作与HashSet同;
public static void main(String[] args) {
LinkedHashSet set = new LinkedHashSet<>();// LinkedHashSet
//
set.add("abcd");
set.add("a");
set.add(1);
set.add(16.66);
set.add("abc");
set.add("abc");
System.out.println(set);
}
TreeSet
定义
TreeSet 是利用红黑树的数据结构来存储元素的。(红黑树是一种平衡二叉树, 在添加和删除元素时, 会根据自身的情况调整树的结构, 而保持树的平衡性。)
注意
add() 的进去的数据:
1. 必须保证是
同一种类型
2.必须是
可排序的(元素必须实现某种协议)即: 实现了 Comparable 接口。
自然排序
实现方法 : 存储的数据类型要实现 Comparable 接口, 重写compareTo 方法
例子(student对象的比较)
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet 可排序的 去重复的 存储的对象数据要实现Comparable接口
TreeSet set = new TreeSet<>;
//当添加一个引用类型的对象数据时会出现ClassCastException是因为没有实现 Comparable接口
set.add(new Student("003", "mo", 10000));
set.add(new Student("001", "zhang", 100));
set.add(new Student("002", "li", 1000));
System.out.println(set);
}
}
//实现可比较接口 【自然排序】
public class Student implements Comparable {
public String id;
public String name;
public double wealth;
public Student(String id, String name, double wealth) {
super();
this.id = id;
this.name = name;
this.wealth = wealth;
}
public Student() {super();}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", wealth=" + wealth + "]";
}
// 重写--实现可比较的方法
@Override
public int compareTo(Object o) {
// 1\根据对象的财富值进行排序
Student stu = (Student) o;
return this.id.compareTo(stu.id);
if(this.wealth - stu.wealth==0) {
return 0;}
return (stu.wealth - this.wealth) > 0 ? 1 : -1;
}
}
// 重写--实现可比较的方法
// 2\根据学号来进行排序
@Override
public int compareTo(Object o) {
Student stu = (Student) o;
return this.id.compareTo(stu.id);
}
定制排序
实现方法 : 定义一个类实现 Comparator 接口, 重写 compare方法
public static void main(String[] args) {
// 创建一个比较器
DocComparator comparator = new DocComparator();
// TreeSet入参comparator,来根据比较器中的规则进行比较
TreeSet set = new TreeSet<>(comparator);
set.add(new Doctor("003", "mo", 10000));
set.add(new Doctor("001", "zhang", 100));
set.add(new Doctor("002", "li", 1000));
System.out.println(set);
}
}
//实现可比较接口
public class Doctor {
public String id;
public String name;
public double wealth;
public Doctor(String id, String name, double wealth) {
super();
this.id = id;
this.name = name;
this.wealth = wealth;
}
public Doctor() {
super();}
@Override
public String toString() {
return "Doctor [id=" + id + ", name=" + name + ", wealth=" + wealth + "]";
}
}
//定义一个类作为比较器类
public class DocComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
// 根据id来进行排序
Doctor doc1 = (Doctor) o1;
Doctor doc2 = (Doctor) o2;
return doc1.id.compareTo(doc2.id);
}
}
foreach 和迭代器中的remove/add方法,一般用迭代器中的
Queue集合
PriorityQueue(单向队列):先进先出
add添加数据
(添加时,要么用add要么用offer 不然会乱序,不是按照原本插入顺序;)
PriorityQueue que = new PriorityQueue<>();
// 建议使用add进行添加数据操作
que.add("1");
// offer方法是有优先级 que.offer("1");
poll获取并移除头部元素
PriorityQueue que = new PriorityQueue<>();
System.out.println(que.poll());//获取队列中的第一个,并删除 peek是获取但不删除
Deque:双向队列(LinkedList、ArrayDeque)
1.Deque 是 Queue 子接口
2.可当栈来使用
push(), pop()
3.在单向队列的基础上扩展, 双头出入队的功能
入队
addFirst(), offerFirst()
addLast(), offerLast()
出队
pollFirst(), pollLast()
removeFirst(), removeLast()
获取元素
getFirst(), getLast()
peekFirst(), peekLast()//peek是获取但不删除,poll是获取并刪除
public static void main(String[] args) {
// ArrayDeque deque = new ArrayDeque();//ArrayDeque底层实现是数组
LinkedList deque = new LinkedList();//LinkedList底层实现是链表
// 队列头部的操作方法
deque.addFirst("");
deque.removeFirst();// 删除第一个数据
// 队列末尾的操作方法
deque.addLast("");
deque.removeLast();// 删除最后一个数据
// poll 获取并删除的操作
deque.pollFirst();
deque.pollLast();
}
Map集合
定义
map 是指一种用来存放映射关系数据的容器。
映射关系
映射关系是指用一个键(key), 与一个值(value)所组成的关系对一个键对应到一个值, 这个键是不能重复的,所以, 通过键就能找到对应的值。
HashMap
定义
无序,不能重复(key不能重复,如果有出现重复的则后面的覆盖前面的数据)
操作
public static void main(String[] args) {
//HashSet 底层是 HashMap的key
// 集合中存放的是对象
// 是一个键值对映射关系的数据集 [无序的、key去重复的 、value可以重复]
HashMap map = new HashMap<>();
// 添加一个键值对数据
map.put("一等奖", "冰箱");
map.put("二等奖", "热水器");
map.put("三等奖", "电饭煲");
map.put("四等奖", "烧水机");
map.put("五等奖", "烧水机");
// 根据key获取value
System.out.println(map.get("一等奖"));
// 删除操作
map.remove("五等奖");
// 替换
map.replace("四等奖", "烧水机", "蒸蛋机");
//
System.out.println(map);
}
使用put方法添加键值对数据,其中key可以为null
特性
[1] 是一个线程不安全的 Map 实现类
[2] 允许插入值为 null 的 key
遍历操作
1、获取key的集合,再遍历value
//操作集合初始化即上面基础操作代码
// 获取所有value值 map.values();
Set keys = map.keySet();
for (Object key : keys) {
System.out.println("key:" + key + " -- " + map.get(key));
}
2、通过Map的内部类Entry进行遍历
// entry封装了map的单个key和value 比如 三等奖=电饭煲
Set entrys = map.entrySet();
//
for (Object object : entrys) {
//
Map.Entry entry = (Entry) object;
//
System.out.println("key:" + entry.getKey() + " -- " + entry.getValue());
}
3、获取key的集合,再通过迭代器遍历value
Iterator iterator = keys.iterator();
while (iterator.hasNext()) {
Object key = iterator.next();
System.out.println("key:" + key + " -- " + map.get(key));
}
练习
人: 国籍 姓名 指纹编码 性别 (属性)
数据信息中心 保存人的数据 【定义规则:指纹编码一样的,则认为同一个人】
有多条数据批量插入到数据信息中心【确保数据不重复-指纹编码】
数据信息中心记录格式: 指纹编码 -- 姓名,国籍,性别 【如果这个人是双重国籍,则追加信息】{姓名,国籍,性别 ; 姓名,国籍,性别}
public class Person {
public String id;
public String name;
public double wealth;
public Person(String id, String name, double wealth) {
super();
this.id = id;
this.name = name;
this.wealth = wealth;
}
public Person() {
super();}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", wealth=" + wealth + "]";
}
}
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
// 数据信息中心 保存人的数据 【定义规则:指纹编码一样的,则认为同一个人】
public class DataCenter {
// 数据集
static HashMap map = new HashMap<>();
// 有多条数据批量插入到数据信息中心【确保数据不重复-指纹编码】
public static void addData(People p) {
// 判断数据是否已经存在
if (map.containsKey(p.getCode())) {// 数据已经存在了\例子: 张三 中国 男
//
String value = (String) map.get(p.getCode());
// 1、指纹编码数据是一致的
if (value.contains(p.toString())) {
return;
} else {
// 2、指纹编码数据是不一致的
map.put(p.getCode(), value + ";" + p.toString());
}
} else//数据不存在,直接加
// 数据信息中心记录格式: 指纹编码 -- 姓名,国籍,性别 【如果这个人是双重国籍,则追加信息】{姓名,国籍,性别 ; 姓名,国籍,性别}
map.put(p.getCode(), p.toString());
}
// 测试代码:
public static void main(String[] args) {
//调用方法添加对象数据
addData(new People("sdgtrtr", "tom", "American", "male"));
addData(new People("jjmmrroo", "jerry", "China", "male"));
addData(new People("sdgtrtr", "tom_Jing", "sui", "male"));
//获取entrySet
Set entrys = map.entrySet();
// 遍历数据集 Map.Entry
for (Object object : entrys) {
Map.Entry entry = (Entry) object;
System.out.println("key=" + entry.getKey() + " value=" + entry.getValue());
}
}
}
HashTable
定义
无序 不能重复(key不能重复,如果有出现重复的则后面的覆盖前面的数据)
操作与hashMap相似,特别:使用put方法添加键值对数据,其中key不可以为null
public static void main(String[] args) {
// Hashtable是线程安全的,而HashMap是线程不安全的
// 是一个键值对映射关系的数据集 [无序的、key去重复的 、value可以重复]
Hashtable map = new Hashtable<>();
// 添加一个键值对数据
map.put("一等奖", "冰箱");
map.put("二等奖", "热水器");
map.put("三等奖", "电饭煲");
map.put("四等奖", "烧水机");
map.put("五等奖", "烧水机");
// NullPointerException 不可以存储null作为key
// map.put(null, "sdf");
// 根据key获取value
System.out.println(map.get("一等奖"));
//
System.out.println(map);
}
Hashtable 特性
[1] 是一个线程安全的 Map 实现类
[2] 为了保证同步性, 性能比 HashMap 要差
[3] 不允许插入值为 null 的 key
[4] 底层使用散列表的存储方式(与 HashMap一致)
遍历操作
//hashtable 特有的枚举类型
//1. 用于遍历value数值
Enumeration enumeration = map.elements();
// 类似于迭代器的遍历操作
while (enumeration.hasMoreElements()) {
System.out.println(enumeration.nextElement());
}
// 2.用于遍历key
Enumeration keys = map.keys();
while (keys.hasMoreElements()) {
System.out.println(keys.nextElement());
}
LinkedHashMap和TreeMap
LinkedHashMap通过链表来维护了数据插入的顺序 (和LinkedHashSet类似)
TreeMap通过二叉树结构进行存储(和TreeSet类似),是根据key来进行排序的。注意TreeMap中key需要实现可比较接口,value不需要。
public class People{
private String code;
private String name;
private String contry;
private String sex;
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getContry() {
return contry;
}
public void setContry(String contry) {
this.contry = contry;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public People(String code, String name, String contry, String sex) {
super();
this.code = code;
this.name = name;
this.contry = contry;
this.sex = sex;}
public People() {
super();
}
@Override
public String toString() {
return "[ name=" + name + ", contry=" + contry + ", sex=" + sex + "]";
}
}
import java.util.Comparator;
import java.util.TreeMap;
public class TreeMapDemo {
public static void main(String[] args) {
// TreeMap 是根据key来进行排序
TreeMap map = new TreeMap<>(new Comparator() {//定制排序 匿名内部类
@Override
public int compare(Object o1, Object o2) {
//
People p1 = (People) o1;
People p2 = (People) o2;
// 则根据唯一标识进行排序
return p1.getCode().compareTo(p2.getCode());
}
});
// map.put("p1",new People("sdgtrtr", "tom", "MiGuo", "male"));
// map.put("p2",new People("sdgtrtr", "tom", "MiGuo", "male"));//可排序的,根据key值排序
map.put(new People("sdgtrtr", "tom", "MiGuo", "male"), "p1");//不可排序,无key值,若要排序用自然排序或定制排序
map.put(new People("jjmmrroo", "jerry", "China", "male"), "p2");
map.put(new People("sdfdsrereytr", "tom", "MiGuo", "male"), "p3");
//
System.out.println(map);
}
}
集合工具类
--排序Collections.sort(list);
--反转Collections.reverse(list);
--洗牌Collections.shuffle(list);
--交换Collections.swap(list, 1, 3);
--交换所有i处和j处的元素
--替换Collections.replaceAll(list, "123", "789");
--替换所有old的元素
--二分查找Collections.binarySearch(list, "12.33");
--集合需要正序排序
--复制操作Collections.copy(dest, src); 其中来源集合src的长度要小于dest复制的目标集合的长度 copy(dest, src);
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class CollectionTools {
public static void main(String[] args) {
// 集合工具类
List list = new ArrayList<>();
//
list.add(10.0);// Integer
list.add(150.0);
list.add(100.0);
list.add(1.66);
list.add(3.66);
// 排序
Collections.sort(list);
System.out.println(list);
// 反转
Collections.reverse(list);
System.out.println(list);
// 洗牌:打乱
// Collections.shuffle(list);
// System.out.println(list);
// 交换 指定位置进行交换
Collections.swap(list, 0, 1);
System.out.println(list);
// 替换
Collections.replaceAll(list, 100.0, 110.0);
System.out.println(list);
//二分查找 : 前提-升序
Collections.sort(list);
System.out.println(list);
System.out.println(Collections.binarySearch(list, 1.66));
List list1 = new ArrayList<>();//复制
list1.add("1");
list1.add("12");
list1.add("13");
list1.add("14");
list1.add("15");
list1.add("16");
list1.add("17");
// 目标集合 ,来源集合 ---将list复制到list1中
Collections.copy(list1 , list);
// 目标集合的长度 > 来源集合的长度
System.out.println(list1);
}
}
排序
//(list中不可比较时,需要重写compare)
public static void main(String[] args) {
List list = new ArrayList<>();
list.add(new People("sdgtrtr", "tom", "MiGuo", "male"));
list.add(new People("jjmmrroo", "jerry", "China", "male"));
list.add(new People("sdgtrtr", "tom_Jing", "sui", "male"));
// 排序的操作 sort(list, Comparator)--重载方法 或sort(list)--类型要对应
Collections.sort(list, new Comparator() {
@Override
public int compare(Object o1, Object o2) {
//
People p1 = (People) o1;
People p2 = (People) o2;
//
return p1.getCode().compareTo(p2.getCode());
}
});
System.out.println(list);}