集合
1 Collection接口
Collection接口是所有集合的父接口。
集合是一个存储数据的容器,大小不固定,每一个数据称之为元素。
public static void main(String[] args) {
// 父接口 对象 = new 实现类();
Collection<String> collection = new ArrayList<>();
// 向集合中添加数据 添加成功返回true
collection.add("丁真");
collection.add("胡歌");
collection.add("赵丽颖");
collection.add("王宝强");
collection.add("梅梅");
collection.add("防弹少年团");
// 清空集合
// collection.clear();
// 判断是否包含某个元素
boolean contains = collection.contains("马蓉");
System.out.println(contains);
// 判断集合是否为空
boolean empty = collection.isEmpty();
System.out.println(empty);
boolean remove = collection.remove("丁真");
System.out.println(remove);
// 获取集合的元素个数
int size = collection.size();
System.out.println(size);
System.out.println(collection);
// 把集合转换成数组
String[] objects = collection.toArray(new String[0]);
for (String object : objects) {
System.out.println(object);
}
}
2 List接口
List接口的特点:
- 有序的集合
- 方法具有索引
- 允许存储重复的元素
List接口的三大实现类: ArrayList LinkedList Vector
2.1 List接口带索引的方法
public static void main(String[] args) {
// 创建接口
List<String> list = new ArrayList<>();
// 添加元素
list.add("李易峰");
list.add("吴亦凡");
list.add("罗志祥");
list.add("陈冠希");
list.add("郑爽");
list.add("范冰冰");
list.add("柯震东");
list.add("柯震东");
list.add("柯震东");
// 插入元素
// IndexOutOfBoundsException
list.add(3,"宋东野");
// 删除指定索引处的元素
list.remove(4);
// 删除指定元素
list.remove("范冰冰");
// 修改指定索引处的元素
list.set(2,"李小璐");
// 截取子列表 包头不包尾
List<String> strings = list.subList(1, 4);
System.out.println(strings);
// 获取指定元素首次出现的索引 找不到就是-1
int index = list.indexOf("柯震恶");
System.out.println(index);
// 获取指定元素最后一次出现的索引
int index1 = list.lastIndexOf("柯震东");
System.out.println(index1);
System.out.println(list);
}
2.2 ArrayList实现类特点
ArrayList底层的数据结构是数组,数组内存是连续的
增删速度慢
查询速度快
线程不安全的集合
默认初始化容量是10,在第一次调用add方法时,会创建长度为10的数组
每次扩容是 原来容量 + 原来容量的一半
2.3 实现ArrayList练习
package cn.javasm.demo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Objects;
/***
* @className: ArrayListDemo
* @author: gfs
* @description: 定义一个类,模拟ArrayList
* @date: 2024/6/22 11:02
* @version: 0.1
* @since :jdk11
*/
public class ArrayListDemo {
// 存储数据的数组
private String[] data;
// 实际元素个数
private int size;
public ArrayListDemo(){
// 默认数组长度为10
data = new String[10];
}
// 定义有参构造,用户可以自定义初始化容量
public ArrayListDemo(int len){
if (len < 0) throw new IllegalArgumentException();
data = new String[len];
}
// 添加元素
public void add(String str){
// 判断数组是否需要扩容
if (size >= data.length){
grow();
}
data[size++] = str;
}
// 数组扩容方法
private void grow() {
if (data.length <= 1){
data = Arrays.copyOf(data,data.length + 1);
}else {
data = Arrays.copyOf(data,data.length + (data.length >> 1));
}
}
// 插入元素
public void add(int index,String str){
// 判断索引是否越界
if (index > size || index < 0){
throw new IndexOutOfBoundsException("数组越界了~");
}
// 是否需要扩容
if (size >= data.length){
grow();
}
// 插入数据
// 将数据向后挪动一位
// for (int i = size - 1;i >= index;i--){
// data[i + 1] = data[i];
// }
// 方法二
System.arraycopy(data,index,data,index + 1,size - index);
// 给索引处赋值
data[index] = str;
// 实际个数 +1
size++;
}
// 删除指定索引处的元素
public void remove(int index){
// 判断是否越界
out(index);
// 删除元素就是将后面的元素向前移动一位
// 方法一:
// for (int i = index;i < size - 1;i++){
// data[i] = data[i + 1];
// }
// 方法二:
System.arraycopy(data,index + 1,data,index,size - index - 1);
// 最后一位设置为默认值
data[size - 1] = null;
size--;
}
private void out(int index) {
if (index < 0 || index >= size){
throw new IndexOutOfBoundsException();
}
}
// 查找指定元素首次出现的索引
public int indexOf(String str){
for (int i = 0;i < size;i++){
if (Objects.equals(str, data[i])){
return i;
}
}
// 如果没有找到,返回-1
return -1;
}
// 删除指定元素代码
public void remove(String str){
// 获取要删除元素的索引
int i = indexOf(str);
if (i != -1){
// 找到了指定的元素,根据索引删除
remove(i);
}
}
// 清空集合
public void clear(){
data = new String[10];
size = 0;
}
// 判断是否包含指定的元素
public boolean contains(String str){
return indexOf(str) != -1;
}
// 获取元素
public String get(int index){
// 判断是否越界
out(index);
return data[index];
}
// 判断集合是否为空
public boolean isEmpty(){
return size == 0;
}
// 替换元素
public void set(int index,String str){
// 判断是否越界
out(index);
data[index] = str;
}
// 获取元素个数
public int size(){
return size;
}
// 截取子列表
public ArrayListDemo subList(int fromIndex,int toIndex){
// 判断参数是否合法
if (fromIndex > toIndex){
throw new IllegalArgumentException();
}
// 判断是否越界
out(fromIndex);
out(toIndex);
// 截取新的列表
ArrayListDemo subList = new ArrayListDemo(toIndex - fromIndex);
// 把数据拷贝到新的列表中
System.arraycopy(data,fromIndex,subList.data,0,toIndex - fromIndex);
// 设置新列表的长度
subList.size = toIndex - fromIndex;
return subList;
}
@Override
public String toString() {
// [李易峰, 吴亦凡, 罗志祥]
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
for (int i = 0; i < size; i++) {
if (i != size - 1){
stringBuilder.append(data[i]).append(", ");
}else {
stringBuilder.append(data[i]);
}
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}
2.4 LinkedList特点
- 底层数据结构是双向链表
- 增删速度快
- 查询速度慢
- 是一个线程不安全的集合
2.5 实现LinkedList练习
package cn.javasm.demo;
import java.util.Objects;
/***
* @className: LinkedListDemo
* @author: gfs
* @description:
* @date: 2024/6/22 15:56
* @version: 0.1
* @since :jdk11
*/
public class LinkedListDemo {
private class Node{
// 数据
String item;
// 上一个结点
Node prev;
// 下一个结点
Node next;
public Node(String item, Node prev, Node next) {
this.item = item;
this.prev = prev;
this.next = next;
}
}
// 实际元素个数
private int size;
// 头结点
private Node first;
// 尾结点
private Node last;
public LinkedListDemo(){}
// 添加元素
public void add(String str){
// 创建结点
Node node = new Node(str,null,null);
// 如果是第一个结点
if (size == 0){
this.first = node;
}else {
// 不是第一个结点
node.prev = this.last;
this.last.next = node;
}
this.last = node;
size++;
}
// 插入元素
public void add(int index,String str){
// 判断索引是否越界
out(index);
// 插入元素分为三种情况
// 1.插入到尾部
if (index == size){
add(str);
return;
}
// 2.插入到头部
// 创建新结点
Node node = new Node(str,null,null);
if (index == 0){
node.next = this.first;
this.first.prev = node;
this.first = node;
}else {
// 3.插入到中间
// 查询要插入索引的结点
// 获取头结点,一个个向下查询
Node no = getNode(index);
no.prev.next = node;
node.prev = no.prev;
node.next = no;
no.prev = node;
}
size++;
}
// 删除指定索引处的元素
public void remove(int index){
// 判断索引是否越界
out(index);
// 删除头结点
if (index == 0){
if (size == 1){
this.first = null;
this.last = null;
}else {
// 头结点设置为当前头结点的下一个
this.first = this.first.next;
// 新的头结点的prev设置为null
this.first.prev = null;
}
}else if (index == size - 1){
// 删除尾部结点
if (size == 1) {
this.first = null;
this.last = null;
}else {
this.last = this.last.prev;
this.last.next = null;
}
}else {
// 根据索引寻找结点
Node no = getNode(index);
no.prev.next = no.next;
no.next.prev = no.prev;
}
// 实际元素个数-1
size--;
}
private Node getNode(int index) {
if (index < size / 2){
// 从头结点向后找
Node no = first;
for (int i = 0; i < index; i++) {
no = no.next;
}
return no;
}else {
// 从尾结点向前找
Node no = last;
for (int i = size - 1; i > index; i--) {
no = no.prev;
}
return no;
}
}
private void out(int index) {
if (index > size || index < 0){
throw new IndexOutOfBoundsException();
}
}
// 查询指定元素结点对应的索引
public int indexOf(String str){
// 获取头结点
Node node = this.first;
for (int i = 0; i < size; i++) {
if (Objects.equals(node.item, str)){
return i;
}
node = node.next;
}
return -1;
}
// 删除指定元素的结点
public void remove(String str){
// 获取首次出现的索引
int index = indexOf(str);
if (index != -1){
remove(index);
}
}
// 清空集合
public void clear(){
// 将头结点和尾结点设置为null
this.first = this.last = null;
// 实际个数为0
size = 0;
}
// 判断是否包含某一个元素
public boolean contains(String str){
return indexOf(str) != -1;
}
// 获取指定索引元素
public String get(int index){
// 判断越界
out(index);
return getNode(index).item;
}
// 判断链表是否为空
public boolean isEmpty(){
return size == 0;
}
// 替换元素
public void set(int index,String str){
// 判断越界
out(index);
getNode(index).item = str;
}
// 获取元素个数
public int size(){
return size;
}
// 截取子链表
public LinkedListDemo subList(int fromIndex,int toIndex){
// 判断越界
out(fromIndex);
out(toIndex);
// 判断参数是否合法
if (fromIndex > toIndex){
throw new IllegalArgumentException();
}
// 创建子链表
LinkedListDemo subList = new LinkedListDemo();
// 获取开始索引处的结点
Node node = getNode(fromIndex);
for (int i = fromIndex;i < toIndex;i++){
// 添加结点的数据到新链表中
subList.add(node.item);
node = node.next;
}
subList.size = toIndex - fromIndex;
return subList;
}
@Override
public String toString() {
// [1, 3, 4]
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("[");
Node node = this.first;
for (int i = 0; i < size; i++) {
if (i != size - 1){
stringBuilder.append(node.item).append(", ");
}else {
stringBuilder.append(node.item);
}
node = node.next;
}
stringBuilder.append("]");
return stringBuilder.toString();
}
}
2.6 Vector实现类
特点:
-
内部数据结构数组
-
内存连续
-
增删慢
-
查询快
-
线程安全的集合
-
默认初始化长度10
-
默认扩容是容量翻倍
-
如果向量增量不是0,那么扩容是原来容量+向量增量
public class VectorDemo {
public static void main(String[] args) {
// 创建Vector
Vector<String> vector = new Vector<>(4,5);
vector.add("长城炮");
vector.add("野马");
System.out.println(vector);
// 获取向量集合的容量
System.out.println(vector.capacity());
// 获取迭代器 用于遍历集合
// 这个是比较古老的迭代器,已经被Iterator替代
Enumeration<String> elements = vector.elements();
while (elements.hasMoreElements()){
String s = elements.nextElement();
System.out.println(s);
}
}
}
2.7 迭代器
public class TestDemo2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
// 添加元素
list.add("苹果");
list.add("小米");
list.add("华为");
list.add("oppo");
list.add("vivo");
list.add("黑莓");
list.add("联想");
list.add("三星");
System.out.println(list);
// 获取迭代器
// Iterator<String> iterator = list.iterator();
// while (iterator.hasNext()){
// String next = iterator.next();
// System.out.println(next);
// // 一边迭代一边删除
//// iterator.remove();
//// list.remove(next);
// }
// 增强for循环也不可以一边迭代一边删除
// for (String s : list) {
// list.remove(s);
// }
System.out.println(list);
// 遍历集合
// list.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// // 会自动把每一个元素传入到这个方法中
// System.out.println(s);
// }
// });
// list.forEach(s -> System.out.println(s));
list.forEach(System.out::println);
}
}
2.8 Stack(栈结构)
特点:
- 是Vector的子类
- 内部数据结构数组
- 内存连续
- 增删慢
- 查询快
- 线程安全的集合
从栈顶到栈底称之为入栈/压栈
从栈底到栈顶称之为出栈/弹栈
public static void main(String[] args) {
Stack<String> stack = new Stack<>();
// 判断集合是否为空
boolean empty = stack.empty();
System.out.println(empty);
// 入栈/压栈
stack.push("美的");
stack.push("格力");
stack.push("海尔");
stack.push("王中王");
stack.push("冰红茶");
stack.push("思念");
System.out.println(stack);
// 出栈/弹栈
String pop = stack.pop();
System.out.println(pop);
System.out.println(stack);
String peek = stack.peek();
System.out.println(peek);
System.out.println(stack);
// 从栈顶向栈底搜索 其实位置是1
int i = stack.search("格力");
System.out.println(i);
}
实现Stack练习
class StackDemo{
// 底层是数组
private String[] data = new String[10];
// 实际元素个数
private int size;
// 判断是否为空
public boolean empty(){
return size == 0;
}
// 返回栈顶的元素
public String peek(){
if (size == 0){
throw new EmptyStackException();
}
// 不为空 返回数组最后一个元素
return data[size - 1];
}
// 返回并删除栈顶元素
public String pop(){
// 返回栈顶元素
String str = peek();
// 忽略栈顶元素
size--;
return str;
}
// 压栈
public String push(String str){
// 判断是否需要扩容
if (size >= data.length){
data = Arrays.copyOf(data,data.length * 2);
}
data[size++] = str;
return str;
}
// 从栈顶到栈底进行搜索元素,并且从1开始
public int search(String str){
// for (int i = size - 1,j = 1;i >= 0;i--,j++){
// if (Objects.equals(str,data[i])){
// return j;
// }
// }
for (int i = size - 1;i >= 0;i--){
if (Objects.equals(str,data[i])){
return size - i;
}
}
return -1;
}
}
2.9 List接口的sort方法
public static void main(String[] args) {
// 创建一个列表
List<String> list = new ArrayList<>();
// 添加元素
list.add("uzi");
list.add("ufo");
list.add("ak");
list.add("mp4");
list.add("dongfeng");
list.add("aiguozhe");
// 给集合中的元素首字符进行排序
// 通过匿名内部类来实现Comparator接口
// list.sort(new Comparator<String>() {
// // 重写接口中的compare方法
// // 返回值int如果是负值,那么参数o1排在o2前面
// // 返回值int如果是正值,那么参数o2排在o1前面
// @Override
// public int compare(String o1, String o2) {
// return o1.charAt(0) - o2.charAt(0);
// }
// });
list.sort((o1,o2)-> o1.charAt(0) - o2.charAt(0));
System.out.println(list);
}
2 Set接口
特点:
不包含重复的元素
2.1 HashSet
特点:
- 不包含重复的元素
- 无序
- 元素位置可能发生改变
- 线程不安全的
- 底层是基于HashMap进行存储
- HashMap底层的数据结构是数组+链表+红黑树
- 默认底层数组初始容量是16,加载因子0.75
- 链表长度大于8,会扭转成红黑树,提升查询效率
2.2 LinkedHashSet
Set接口的实现类,是HashSet的子类
特点:
- 有序的集合
- 底层数据结构是哈希表 + 双向链表
- 线程不安全的集合
2.3 TreeSet
// TreeSet会将元素进行排序
// Set<String> set = new TreeSet<>();
// set.add("k");
// set.add("ak");
// set.add("aa");
// set.add("d");
// set.add("l");
// System.out.println(set);
// 放入TreeSet中的元素对应的类要实现Comparable接口
Set<Student> set = new TreeSet<>();
set.add(new Student("郭靖",40,95));
set.add(new Student("杨过",20,90));
set.add(new Student("张无忌",15,99));
set.add(new Student("萧峰",45,100));
set.forEach(System.out::println);
}
}
class Student implements Comparable<Student>{
private String name;
private int age;
private int score;
public Student(String name, int age, int score) {
this.name = name;
this.age = age;
this.score = score;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
'}';
}
// 拿当前的对象和传入的对象进行比较
// 如果返回值是正值,那么当前对象放在传入对象的后面
// 如果返回值是负值,那么当前对象放在传入对象的前面
@Override
public int compareTo(Student o) {
return this.score - o.score;
}
}
3 Queue接口
Queue是队列,是FIFO(first in first out 先进先出)
public static void main(String[] args) {
Queue<String> queue = new LinkedList<>();
// 添加元素
queue.add("abc");
queue.add("cbd");
queue.add("haha");
queue.offer("aaa");
// 获取元素 element 如果队列中没有元素,抛出NoSuchElementException 没有元素异常
// String element = queue.element();
// System.out.println(element);
// 获取元素 peek 如果队列中没有元素,返回null
// String peek = queue.peek();
// System.out.println(peek);
// 获取并删除头部元素 如果队列中没有元素,抛出NoSuchElementException 没有元素异常
// String remove = queue.remove();
// System.out.println(remove);
// 获取并删除头部元素 如果队列中没有元素,返回null
String poll = queue.poll();
System.out.println(poll);
System.out.println(queue);
}
4 泛型
泛型是jdk5的特性,能够保证程序的安全性,减少代码量,避免类型强制转换。
泛型必须是引用数据类型
泛型可以使用在类,接口,方法上
泛型的使用
类名/接口名<>
泛型在命名的时候只要是遵循标识符的命名规则都可以
习惯上通过一个大写字母来表示泛型
E element 元素
T type 类型
R result 结果
K key 键
V value 值
4.1 泛型类
在类上面使用泛型
public class Person <A,B> {
private A name;
private B age;
public B getAge() {
return age;
}
public void setAge(B age) {
this.age = age;
}
public A getName() {
return name;
}
public void setName(A name) {
this.name = name;
}
}
使用:
public static void main(String[] args) {
// 创建对象时进行泛型擦除
Person<String,Integer> person = new Person<>();
person.setName("张三");
person.setAge(18);
}
4.2 接口中的泛型
- 实现接口不实现泛型
public interface MyInter <E>{
void test(E e);
}
public class MyInterImpl<E> implements MyInter<E>{
@Override
public void test(E e) {
System.out.println(e);
}
}
public static void main(String[] args) {
// 创建对象时进行泛型擦除
MyInter<String> myInter = new MyInterImpl<>();
myInter.test("gfs");
}
- 实现接口,指定泛型的数据类型
public interface MyInter <E>{
void test(E e);
}
public class MyInterImpl2 implements MyInter<Integer>{
@Override
public void test(Integer integer) {
System.out.println(integer + 1);
}
}
public static void main(String[] args) {
// 创建对象时进行泛型擦除
MyInterImpl2 myInter = new MyInterImpl2();
myInter.test(10);
}
4.3 方法中的泛型
public <K> K add(K k){
return k;
}