引言
之前我们使用数组保存多个数据,但是,数组有一些不足的地方:
- 数组长度开始时必须指定,而且一旦指定,不能更改
int[] a = new int[3];
- 保存的必须为同一类型的元素
- 使用数组进行增加元素比较麻烦
与之对应的,集合就没有这些缺点,集合相比于数组,有以下优点:
- 可以动态保存任意多个对象
- 提供了一系列方便的操作对象的方法: add(增)、remove(删)、set(改)、get(查)等
- 使用集合添加,删除新元素十分简洁
集合体系
虚线实现接口,实线继承类
单列集合
双列集合
代码演示
package cs.kaoyan.javase.com;
import java.util.ArrayList;
import java.util.HashMap;
public class Test {
//添加注解,抑制警告
@SuppressWarnings({"all"})
public static void main(String[] args) {
//单列集合演示:添加元素的时候是单列的
ArrayList arrayList = new ArrayList();
arrayList.add("java");
arrayList.add("se");
arrayList.add("easy");
//双列集合演示:添加元素的时候是双列的
HashMap hashMap = new HashMap();
hashMap.put("no1","java");
hashMap.put("no2","c++");
hashMap.put("no3","python");
}
}
Collection接口和常用方法
Collection接口实现类的特点:
public interface Collection<E> extends Iterable<E>
- collection实现子类可以存放多个元素,每个元素可以是Object
- Collection的实现类,可以存放重复的元素,有些不可以
- Collection的实现类,有些是有序的(比如List),有些不是有序(比如Set)
- Collection接口没有直接的实现子类,是通过它的子接口Set 和List来实现的
代码演示
package cs.kaoyan.javase.com;
import java.util.ArrayList;
import java.util.List;
public class Test {
//添加注解,抑制警告
@SuppressWarnings({"all"})
public static void main(String[] args) {
List list = new ArrayList();
//字符串
list.add("java");
//整型
list.add(10);
//boolean
list.add(true);
//[java, 10, true]
System.out.println(list);
//查找元素是否存在
//存在则返回true
System.out.println(list.contains("java"));
//删除下标所在的元素
list.remove(0);
//删除指定元素
list.remove(true);
//[10]
System.out.println(list);
//获取集合中元素的个数
//1
System.out.println(list.size());
//判断集合是否非空
//false
System.out.println(list.isEmpty());
//清空集合
list.clear();
//0
System.out.println(list.size());
//创建一个新集合arrayList
ArrayList arrayList = new ArrayList();
arrayList.add("迪迦奥特曼");
arrayList.add(100);
//向集合list中添加多个元素
list.addAll(arrayList);
//[迪迦奥特曼, 100]
System.out.println(list);
list.add("acm-icpc");
//删除多个元素
list.removeAll(arrayList);
//[acm-icpc]
System.out.println(list);
}
}
使用迭代器遍历集合元素
- lterator对象称为迭代器,主要用于遍历Collection集合中的元素
- 仅用于遍历集合,lterator本身并不存放对象
- 需要注意的是:在调用next方法之前必须要调用hasNex进行检测。若不调用且下一条记录无效,直接调用next方法会抛出NoSuchElementException异常
package cs.kaoyan.javase.com;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
public class Test {
//添加注解,抑制警告
@SuppressWarnings({"all"})
public static void main(String[] args) {
//父类引用指向子类对象(向上转型)
Collection coll = new ArrayList();
coll.add(new Book("计算机网络","长风",18.0));
coll.add(new Book("数据结构","咸鱼",28.0));
coll.add(new Book("操作系统","天明",38.0));
//先得到coll对应的迭代器
Iterator iterator = coll.iterator();
//使用while循环遍历
//需要先判断是否还有数据
while (iterator.hasNext()){
//获取数据
Object next = iterator.next();
System.out.println(next);
}
/*Book{name='计算机网络', author='长风', price=18.0}
Book{name='数据结构', author='咸鱼', price=28.0}
Book{name='操作系统', author='天明', price=38.0}*/
}
}
class Book{
private String name;
private String author;
private double price;
//构造器
public Book(String name, String author, double price) {
this.name = name;
this.author = author;
this.price = price;
}
//重写toString
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' +
", author='" + author + '\'' +
", price=" + price +
'}';
}
}
模拟List,建立自己的"List",并用快慢指针取这个链表的倒数第x个结点
-
要求1:模拟List,建立自己的"List",然后实现对链表元素的增删改查
-
要求2:实现一个方法,接收一个链表的头结点和int类型值x,取这个链表的倒数第x个结点内容
-
第一步:先模拟链表的建立
package cs.kaoyan.javase.com;
//抑制所有警告
@SuppressWarnings({"all"})
public class MyLinkList {
private Node head;//单链表的头结点
private int size;//记录链表存储了多少数据
/**
* 单链表的添加方法
* @param value:要添加的值
* @return:添加是否成功
*/
public boolean add(String value){
//不允许插入null
if (value == null){
return false;
}
//将新元素添加到头结点后面
//判断链表是否为空
if (isEmpty()) {
//链表为空(head -> null),新建一个结点,插入表头
Node node = new Node(value);
//不带头结点的单链表
head = node;
//以上两行代码可以简写为 : head = new Node(value);
//插入新结点之后链表长度加1
size++;
//插入成功
return true;
}
//代码执行到这里说明链表不为空,此时默认在表尾插入
//要在表为插入数据,需要先找到表尾元素
//在对链表的操作中,除非特殊要求需要操作头指针,其余情况不要对头指针进行操作
//否则容易造成GC回收资源,从而引发错误
//temp做为临时指针
Node temp = head;
//使用while循环找到链表的最后一个元素
while (temp.next != null){
//只要下一个元素不为null,就可以后移指针
temp = temp.next;
}
//代码执行到这里,说明已经到达了链表末尾
//此时可以新建结点,然后接入链表
Node node = new Node(value);
temp.next = node;
//以上两行代码可以简写为 : temp.next = new Node(value);
size++;
return true;
}
/**
* 单链表的删除方法
* @param value:删除的值
* @return:表示删除是否成功
*/
public boolean remove(String value){
//不允许删除null
if (value == null) {
return false;
}
//先判断链表是否为空
if (isEmpty()){
//链表为空,无法删除
return false;
}
//代码执行到这里,表示链表非空,此时定义一个临时指针用来遍历链表
//找到需要删除的元素时,即可删除
//一般不操作链表的头指针,故创建一个临时变量,用于从头遍历链表
Node temp = head;
//删除头结点
if (temp.value.equals(value)){
head = head.next;
size--;
}
//找到待删除结点的前一个结点,修改指针即可
while (temp.next != null && !temp.next.value.equals(value)){
//指针后移
temp = temp.next;
}
//如果指针移动到链表最后,说明待删除元素不存在,返回false
if (temp.next == null){
return false;
}
//执行完while循环,temp指针指向待删除结点的前一个结点
//且此时temp指向的结点不是最后一个结点
//这时移动指针来实现元素删除
temp.next = temp.next.next;
size--;
return true;
}
/**
* 单链表的查找方法
* @param value:待查找的值
* @return:待查找的元素是否存在
*/
public boolean contains(String value){
//链表为空,返回false
if (isEmpty()){
return false;
}
//查找null元素,返回false
if (value == null){
return false;
}
//遍历链表
Node temp = head;
//如果下一个结点不为null
//且当前结点的值不等于要查找的元素值,就可以后移指针
while (temp.next != null && !temp.value.equals(value)){
temp = temp.next;
}
//while循环结束后,temp要么指向最后一个结点,要么已经查找到元素
//指针指向链表最后一个结点,此时temp.next == null
if (!temp.value.equals(value)){
//未查找到指定元素
return false;
}
//代码执行到这里,说明找到了要查找的元素
return true;
}
/**
*
* @param oldValue:要被替换掉的老元素
* @param newValue:用来替换的新元素
* @return
*/
public boolean set(String oldValue,String newValue){
//如果链表为空,那么返回false
if (isEmpty()){
return false;
}
//设置临时指针
Node temp = head;
while (!temp.value.equals(oldValue) && temp != null){
temp = temp.next;
}
if (temp == null){
//遍历完链表,没找到oldValue
return false;
}
//找到需要修改的元素
temp.value = newValue;
return true;
}
public boolean isEmpty(){
//链表的size为0表示链表为空 -> 返回true
return size == 0;
}
//返回链表中倒数第k个结点
/***
* 解题思路:
* 快慢指针,先让快指针走k步,然后两个指针同时走,当快指针到头时,慢指针就是链表倒数第k个节点
* 快慢指针思想的优势:使用双指针可以不用统计链表长度
*/
public Node getKElement(Node head, int k){
if (k < 0 || head == null){
return null;
}
//初始化:快慢指针都指向头节点head
//设置快指针
Node fast = head;
//设置慢指针
Node low = head;
//快指针先往前走k步
while (k > 0 && fast != null){
fast = fast.next;
k--;
}
//快慢指针一起走
while (fast != null){
fast = fast.next;
low = low.next;
}
//最后输出慢指针指向的元素即可
System.out.println(low.value);
return low;
}
//getter方法,获取链表的头结点
public Node getHead() {
return head;
}
//单链表的结点
//内部类:外部无法new对象
class Node{
String value;//值域
Node next;//指针域
//单参构造器
public Node(String value) {
this.value = value;
}
//双参构造器
public Node(String value, Node next) {
this.value = value;
this.next = next;
}
@Override
public String toString() {
return "Node{" +
"value='" + value + '\'' +
", next=" + next +
'}';
}
}
}
- 写测试类
package cs.kaoyan.javase.com;
public class Test {
public static void main(String[] args) {
//创建链表对象
MyLinkList myLinkList = new MyLinkList();
//增 1 -> 2 -> 3 -> 4
myLinkList.add("1");
myLinkList.add("2");
myLinkList.add("3");
myLinkList.add("4");
System.out.println(myLinkList.contains("0"));//false
System.out.println(myLinkList.contains("2"));//true
System.out.println(myLinkList.contains("3"));//true
System.out.println(myLinkList.contains("4"));//true
System.out.println(myLinkList.contains("1"));//true
System.out.println(myLinkList.contains("8"));//false
System.out.println(myLinkList.contains("9"));//false
//删 1 -> 2 -> 3 -> 4 —— 2
myLinkList.remove("1");
myLinkList.remove("3");
myLinkList.remove("4");
//改 2 —— 8
myLinkList.set("2","8");
//增 8 -> 6 -> 7 -> 1 -> 9
myLinkList.add("6");
myLinkList.add("7");
myLinkList.add("1");
myLinkList.add("9");
//调用getter方法,获取链表的头结点
MyLinkList.Node head = myLinkList.getHead();
myLinkList.getKElement(head,2);
System.out.println(myLinkList);
}
}