0 课程地址
https://coding.imooc.com/lesson/207.html#mid=13704
1 重点关注
1.1 使用链表实现集合Set
详见3.1用链表实现的集合
2 课程内容
3 Coding
3.1 使用链表实现集合Set
- 需求
使用链表实现的集合统计 傲慢与偏见 双城记两本书的英文词汇量
- 链表
package com.company; /*** * 链表 * @author weidoudou * @date 2022/10/28 7:56 **/ public class LinkedList<E> { /** * 1 内部类node * @author weidoudou * @date 2022/10/28 7:59 * @return null **/ private class Node{ //Node 只有两个属性,下一个节点和本节点存储的元素 private E e; private Node next; /** * 通用调用node方法 * @author weidoudou * @date 2022/10/28 8:17 * @param e 请添加参数描述 * @param next 请添加参数描述 * @return null **/ public Node(E e,Node next){ this.e = e; this.next = next; } /** * node 无参构造 * @author weidoudou * @date 2022/10/28 8:15 * @return null **/ public Node(){ this(null,null); } /** * node 有参构造 * @author weidoudou * @date 2022/10/28 8:16 * @param e 请添加参数描述 * @return null **/ public Node(E e){ this(e,null); } @Override public String toString() { return e.toString(); } } //2 LinkedList 属性 链表头元素(火车头),大小 private int size; private Node dummyHead; /** * 3 LikedList 无参 * @author weidoudou * @date 2022/10/28 8:27 * @return null **/ public LinkedList() { this.dummyHead = new Node(null,null); this.size = 0; } /** * 4 getSize * @author weidoudou * @date 2022/10/28 8:23 * @return null **/ public int getSize(){ return size; } /** * 5 isEmpyt * @author weidoudou * @date 2022/10/28 8:24 * @return boolean **/ public boolean isEmpty(){ return size == 0; } /** * 6 链表头部添加元素 * @author weidoudou * @date 2022/10/28 8:37 * @param e 请添加参数描述 * @return void **/ public void addFirst(E e){ /*Node nodeNew = new Node(e); nodeNew.next = head; //火车尾指向 上个尾巴 this.head = nodeNew; //火车尾 变成了当前的node*/ //称之为优雅写法 addElement(0,e); } /** * 7 链表尾部添加元素 认真分析下 * @author weidoudou * @date 2022/10/28 18:11 * @param e 请添加参数描述 * @return void **/ public void addLast(E e){ addElement(size,e); } /** * 8 链表添加元素(链表通常不在中间添加元素,编写此段代码完全是为了后续便于理解和二叉树相关知识做铺垫) * @author weidoudou * @date 2022/10/28 8:45 * @param index 请添加参数描述 * @param e 请添加参数描述 * @return void **/ public void addElement(int index,E e){ if(index<0||index>size){ throw new IllegalArgumentException("索引不正确"); } Node pre = dummyHead; for(int i = 0;i<index;i++){ pre = pre.next; } /*Node nodeNew = new Node(e); nodeNew.next = pre.next; pre.next = nodeNew;*/ //优雅写法 pre.next = new Node(e,pre.next); size++; } /** * 9 查询 * @author weidoudou * @date 2022/10/29 11:18 * @param index 请添加参数描述 * @return E **/ public E findByIndex(int index){ if(index<0||index>size-1){ throw new IllegalArgumentException("索引越界"); } Node cur = dummyHead.next; for(int i = 0;i<index;i++){ cur = cur.next; } return cur.e; } /** * A 查询首个元素 * @author weidoudou * @date 2022/10/29 11:27 * @return E **/ public E findByFirst(){ return findByIndex(0); } /** * B 查询最后一个元素 * @author weidoudou * @date 2022/10/29 11:27 * @return E **/ public E findByLast(){ return findByIndex(size-1); } /** * C 修改 * @author weidoudou * @date 2022/10/29 11:29 * @param index 请添加参数描述 * @param e 请添加参数描述 * @return void **/ public void update(int index,E e){ if(index<0||index>size-1){ throw new IllegalArgumentException("索引越界"); } Node cur = dummyHead.next; for(int i = 0;i<index;i++){ cur = cur.next; } cur.e = e; } /** * D 修改第一个元素 * @author weidoudou * @date 2022/10/29 11:31 * @param e 请添加参数描述 * @return void **/ public void updateFirst(E e){ update(0,e); } /** * E 修改最后一个元素 * @author weidoudou * @date 2022/10/29 11:31 * @param e 请添加参数描述 * @return void **/ public void updateLast(E e){ update(size-1,e); } /** * F 查询链表是否包含元素 * @author weidoudou * @date 2022/10/29 13:04 * @param e 请添加参数描述 * @return boolean **/ public boolean contains(E e){ boolean flag; for(Node cur = dummyHead.next;cur!=null;cur=cur.next){ if(e.equals(cur.e)){ return true; } } return false; } /** * G toString * @author weidoudou * @date 2022/10/29 13:06 * @return java.lang.String **/ @Override public String toString() { StringBuilder sb = new StringBuilder(); for(Node cur = dummyHead.next;cur!=null;cur=cur.next){ sb.append(cur+"->"); } sb.append("Null"); return sb.toString(); } /** * H 删除索引为index的元素 * @author weidoudou * @date 2022/10/29 19:40 * @param index 请添加参数描述 * @return E **/ public E remove(int index){ if(index<0||index>size-1){ throw new IllegalArgumentException("索引越界"); } Node pre = dummyHead; for(int i = 0;i<index;i++){ pre = pre.next; } Node ret = pre.next; pre.next = ret.next; ret.next = null; size--; return ret.e; } /** * 删除第一个元素 * @author weidoudou * @date 2022/10/30 14:56 * @return E **/ public E removFirst(){ return remove(0); } /** * 删除最后一个元素 * @author weidoudou * @date 2022/10/30 14:56 * @return E **/ public E removLast(){ return remove(size-1); } // 从链表中删除元素e public void removeElement(E e){ Node prev = dummyHead; while(prev.next != null){ if(prev.next.e.equals(e)) break; prev = prev.next; } if(prev.next != null){ Node delNode = prev.next; prev.next = delNode.next; delNode.next = null; size --; } } }
- Set接口
package com.company;
/**
* 集合接口
* @author weidoudou
* @date 2022/12/14 8:14
* @return null
**/
public interface Set<E> {
/**
* 是否包含
* @author weidoudou
* @date 2022/12/14 8:17
* @param e 请添加参数描述
* @return boolean
**/
boolean contails(E e);
/**
* 是否为空
* @author weidoudou
* @date 2022/12/14 8:18
* @return boolean
**/
boolean isEmpty();
/**
* 获取个数
* @author weidoudou
* @date 2022/12/14 8:19
* @return int
**/
int getSize();
/**
* 添加方法
* @author weidoudou
* @date 2022/12/14 8:19
* @param e 请添加参数描述
* @return void
**/
void add(E e);
/**
* 删除方法
* @author weidoudou
* @date 2022/12/14 8:20
* @param e 请添加参数描述
* @return void
**/
void remove(E e);
}
- LinkedList Set
package com.company; public class LinkedListSet<E> implements Set<E>{ private LinkedList<E> linkedList; public LinkedListSet(){ linkedList = new LinkedList<>(); } @Override public boolean contails(E e) { return linkedList.contains(e); } @Override public boolean isEmpty() { return linkedList.isEmpty(); } @Override public int getSize() { return linkedList.getSize(); } @Override public void add(E e) { if(!linkedList.contains(e)){ linkedList.addFirst(e); } } @Override public void remove(E e) { if(linkedList.contains(e)){ linkedList.removeElement(e); } } }
- 文件处理类:
package com.company;
import java.io.FileInputStream;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.Locale;
import java.io.File;
import java.io.BufferedInputStream;
import java.io.IOException;
// 文件相关操作
public class FileOperation {
// 读取文件名称为filename中的内容,并将其中包含的所有词语放进words中
public static boolean readFile(String filename, ArrayList<String> words){
if (filename == null || words == null){
System.out.println("filename is null or words is null");
return false;
}
// 文件读取
Scanner scanner;
try {
File file = new File(filename);
if(file.exists()){
FileInputStream fis = new FileInputStream(file);
scanner = new Scanner(new BufferedInputStream(fis), "UTF-8");
scanner.useLocale(Locale.ENGLISH);
}
else
return false;
}
catch(IOException ioe){
System.out.println("Cannot open " + filename);
return false;
}
// 简单分词
// 这个分词方式相对简陋, 没有考虑很多文本处理中的特殊问题
// 在这里只做demo展示用
if (scanner.hasNextLine()) {
String contents = scanner.useDelimiter("\\A").next();
int start = firstCharacterIndex(contents, 0);
for (int i = start + 1; i <= contents.length(); )
if (i == contents.length() || !Character.isLetter(contents.charAt(i))) {
String word = contents.substring(start, i).toLowerCase();
words.add(word);
start = firstCharacterIndex(contents, i);
i = start + 1;
} else
i++;
}
return true;
}
// 寻找字符串s中,从start的位置开始的第一个字母字符的位置
private static int firstCharacterIndex(String s, int start){
for( int i = start ; i < s.length() ; i ++ )
if( Character.isLetter(s.charAt(i)) )
return i;
return s.length();
}
}
- 测试类:
public static void main(String[] args) { System.out.println("pride and prejudice"); ArrayList<String> words1 = new ArrayList<>(); FileOperation.readFile("pride-and-prejudice.txt",words1); System.out.println("Total words:"+words1.size()); Set<String> linkedListSet = new LinkedListSet<>(); words1.stream().map(e->{ linkedListSet.add(e); return e; }).collect(Collectors.toSet()); System.out.println("Total different words:"+linkedListSet.getSize()); System.out.println("a tale of two cities"); ArrayList<String> words2 = new ArrayList<>(); FileOperation.readFile("a-tale-of-two-cities.txt",words2); System.out.println("Total words:"+words2.size()); Set<String> linkedListSet2 = new LinkedListSet<>(); words2.stream().map(e->{ linkedListSet2.add(e); return e; }).collect(Collectors.toSet()); System.out.println("Total different words:"+linkedListSet2.getSize()); }
- 测试结果:
---- IntelliJ IDEA coverage runner ----
sampling ...
include patterns:
exclude patterns:
pride and prejudice
Total words:125901
Total different words:6530
a tale of two cities
Total words:141489
Total different words:9944
Class transformation time: 0.02436s for 386 classes or 6.310880829015544E-5s per class
Process finished with exit code 0
诸葛