java链表
一、链表介绍
链表是一种根据元素节点逻辑关系排列起来的一种数据结构。利用链表可以保存多个数据。这里有类似数组的概念,但是数组因为本身的限制有一个很大的缺点——数组的长度固定,不可改变。在长度固定的情况下首选的肯定是数组,但是在实际开发中往往要保存的内容长度是不固定的,此时可以用链表结构来代替数组的使用。
二、链表的基本形式
所有要保存的数据都会被包装到一个节点对象中,之所以会引用一个节点类,是因为只依靠保存的数据无法区分出先后顺序,而引入了Node类可以包装数据并指向下一个节点。所以在Node类的设计中主要保存两个属性:
- 数据(data)
- 下一个节点的引用(next)
Node节点类本身不属于一个简单Java类,而是一个功能性的表示类,在这个类中,主要保存了两种数据:一种是储存的对象,另一种是当前节点的下一个节点
在链表操作的时候,首先需要一个根节点即第一个节点,然后每一个及诶单的引用都保存在上一个节点的next属性中。在进行输出操作的时候也应该按照节点的先后顺序一个一个取得每一个节点所包装的数据。
三、节点类的基本操作
- 新增节点(addNode)
- 打印节点(printNode)
- 构造方法(Node)
1.新增节点:
可以用递归的方法遍历所有节点,直到遍历到最后一个节点,把新的节点放到最后一个节点后面。
1 public void addNode(Node newNode) { 2 if(this.next == null) { 3 this.next = newNode; 4 } 5 else { 6 this.next.addNode(newNode); //递归调用addNode方法 7 } 8 }
在这个方法中,当递归遍历到最后一个节点时,如果他对的next为空则把新节点插入到该位置。
2.打印节点:
可以用递归调用的方法遍历所有节点,没遍历一个节点就输出这个节点的值,直到最后一个节点的next指正为空
1 public void printNode() { 2 System.out.println(this.data); 3 if(this.data != null) { 4 if(this.next == null) { 5 return; 6 } 7 else { 8 this.next.printNode(); 9 } 10 } 11 }
在这个方法中,每次递归到一个节点,输出这个节点的值后,判断这个节点的next值是否为空,如果为空则返回,否则才继续递归。
3.构造方法
Node类中还有一个构造方法,在Node节点创建的时候,这个节点的data会通过构造方法的参数赋值给这个新创建的节点。
1 //构造方法,每一个Node类对象都必须保存有相应的数据 2 public Node(String data) { 3 this.data = data; 4 }
四、链表的基本雏形
链表中最重要的类就是Node,而如果用户直接操作Node类来封装数据,匹配节点关系,会给用户操作带来很大的复杂性,而用户实际上只关心链表中保存的数据有哪些,至于说数据是如何保存的,节点间的的关系是如何分配的,用户完全不需要知道。所以此时应该定义一个专门负责这个节点操作的类。
这个类便是称为链表操作类的Link类
做了一个没有完成的Demo,暂时学到这里吧,心累。。。
1 package question6; 2 import java.io.BufferedReader; 3 import java.io.File; 4 import java.io.FileNotFoundException; 5 import java.io.FileReader; 6 import java.io.FileWriter; 7 import java.io.IOException; 8 import java.io.Reader; 9 import java.io.Writer; 10 import java.util.Scanner; 11 /* 12 * 程序说明: 13 * Book类: 14 * 构造方法(book对象包含title和price属性) 15 * compare(比较title是否相同) 16 * getInfo 17 * Info 18 * Link类(外部能看见的类): 19 * 内部的Node类(用于对节点进行操作) 20 * 构造方法(通过book类对象构造Node) 21 * addNode方法 22 * containsNode方法 23 * getNode方法 24 * setNode方法 25 * removeNode方法 26 * toArrayNode方法 27 * root根节点(Node类对象) 28 * count保存元素的个数(int型) 29 * foot节点索引(int型) 30 * flag 31 * retArray数组(book类型) 32 * add方法 33 * size方法 34 * contains方法 35 * get方法 36 * set方法 37 * remove方法 38 * toArray方法 39 * clear方法 40 * LinkListDemo类 41 * 42 */ 43 44 //定义一个保存图书信息的类 45 class Book{ 46 private String title; 47 private double price; 48 49 /* 50 * 构造函数 51 * 书名和价格 52 */ 53 public Book(String title,double price) { 54 this.title = title; 55 this.price = price; 56 } 57 58 /* 59 * compare函数 60 * 对本类的对象进行比较操作 61 * 1.判断传入的对象是否为null 62 * 2.判断对象的地址是否相同 63 * 3.对对象的内容进行比较 64 */ 65 public boolean compare(Book book) { 66 if(book == null) { 67 return false; 68 } 69 if(this == book) { //如果内存地址相同,则不需要进行细节比较,节约时间 70 return true; 71 } 72 if(this.title.equals(book.title)&&this.price == book.price) { 73 return true; 74 } 75 else { 76 return false; 77 } 78 } 79 80 /* 81 * getInfo函数 82 * 返回当前book对象的名称和价格 83 */ 84 public String getInfo() { 85 return "图书名称:"+ this.title + ",图书价格" + this.price; 86 } 87 88 public String info() { 89 return this.title + "\t" + this.price + "\r\n"; 90 } 91 } 92 //---------------------------Book类结束----------------------------------------- 93 94 //链表的实现类,外部能看见的只有这个类 95 class Link{ 96 //内部节点类 97 private class Node{ 98 private Book data; 99 private Node next; 100 public Node(Book data) { 101 this.data = data; 102 } 103 104 /* 105 * 节点类的addNode函数 106 * 所有节点的新建保存在最后一个节点的尾部 107 */ 108 public void addNode(Node newNode) { 109 //如果当前节点的下一个节点是null,则把新建的节点插入到这个节点之后 110 if(this.next == null) { 111 this.next = newNode; 112 } 113 else {//否则,递归调用这个函数继续向后保存 114 this.next.addNode(newNode); 115 } 116 } 117 /* 118 * 判断某本书籍是否存在函数containsNode 119 * 递归调用自身来遍历链表的每一个节点直到找到该节点,返回true 120 * 1.如果当前节点找到,返回true 121 * 2.如果当前节点未找到,判断下一个节点 122 * 3.如果下一个节点为空则返回true 123 */ 124 public boolean containsNode(Book data) { 125 126 if(data.compare(this.data)) { 127 flag = 1; 128 return true; 129 } 130 else { 131 if(this.next!=null) { 132 return this.next.containsNode(data); 133 } 134 else { 135 flag = 0; 136 return false; 137 } 138 } 139 } 140 141 /* 142 * 根据索引取数据函数getNode 143 * 参数是某个节点的索引值 144 * 返回值是指定索引包含的书籍名称 145 */ 146 public Book getNode(int index) { 147 if(Link.this.foot++ == index) { 148 return this.data; 149 } 150 else { 151 return this.next.getNode(index); 152 } 153 } 154 155 /* 156 * 根据索引修改数据函数setNode 157 * 参数是某个节点的索引和和书籍名 158 * 返回值是新的数据 159 */ 160 public void setNode(int index,Book data) { 161 if(Link.this.foot++ == index) { 162 this.data = data; 163 } 164 else { 165 this.next.setNode(index, data); 166 } 167 } 168 169 /* 170 * 节点的删除操作,removeNode 171 * 遍历每一个节点的数据,如果当前节点符合则删除 172 * 原理:当前节点的上一个节点.next = 当前节点.next 173 * 参数:当前节点的上一个节点和要删除的书籍 174 */ 175 public void removeNode(Node previous,Book data) { 176 if(data.compare(this.data)) { 177 previous.next = this.next; 178 } 179 else { 180 this.next.removeNode(this, data); 181 } 182 } 183 184 /* 185 * 将节点中保存的内容转化为数组函数toArrayNode 186 * 作用是将当前节点中保存的内容转化为对象数组 187 * 1.把当前对象存入retArray数组 188 * 2.递归调用自己,把下一个对象存入retArray 189 * 3.直到链表最后一节点 190 */ 191 public void toArrayNode() { 192 Link.this.retArray[Link.this.foot++] = this.data; 193 if(this.next != null) { 194 this.next.toArrayNode(); 195 } 196 } 197 } 198 //----------------------------------以上为内部类Node-------------------------------------- 199 200 private Node root; //定义根节点 201 private int count = 0; //定义保存元素的个数 202 private int foot = 0; //定义节点索引 203 public int flag = 0; 204 private Book[] retArray; //返回的数组 205 206 /* 207 * 用户向链表增加新的数据 208 * 参数是Node类对象 209 * 1.错误判断,如果传入数据为空则返回 210 * 2.定义一个newNode节点对象保存传入的对象 211 * 3.判断当前链表的根节点是否为空 212 * 4.如果为空则把新节点当做根节点 213 * 5.如果不为空,则用根节点对象调用节点类内部函数addNode 214 * 6.将newNode对象传入内部类函数创建新节点 215 */ 216 public void add(Book data) { 217 if(data == null) { 218 return; 219 } 220 Node newNode = new Node(data); 221 if(this.root == null) { 222 this.root = newNode; 223 } 224 else 225 { 226 this.root.addNode(newNode); 227 } 228 this.count++; 229 } 230 231 /* 232 * 获取当前链表数据个数函数 233 * 返回值是当前链表的数据个数 234 */ 235 public int size() { 236 return this.count; 237 } 238 239 /* 240 * 判断当前链表是否存在某个节点函数contains 241 * 1.判断传入的数据是否为空或当前链表是否为空,只要有一个节点为空,则返回false 242 * 2.否则,则用根节点调用内部类函数containsNode函数遍历节点列表判断是否存在该节点 243 */ 244 public boolean contains(Book data) { 245 flag = 0; 246 if(data == null || this.root == null) { 247 return false; 248 } 249 return this.root.containsNode(data); 250 } 251 252 /* 253 * 根据索引取数据函数get 254 * 1.判断传入的索引是否大于当前链表长度 255 * 2.大于则返回空 256 * 3.否则,设置foot值为0,调用内部类函数getNode遍历节点列表 257 */ 258 public Book get(int index) { 259 if(index > this.count) { 260 return null; 261 } 262 this.foot = 0; 263 return this.root.getNode(index); 264 } 265 266 public int getCount() { 267 return this.count; 268 } 269 /* 270 * 根据节点索引修改节点的数据 271 * 如果索引值大于当前节点的长度直接返回 272 * 否则调用内部类函数setNode遍历查找需要修改的数据 273 */ 274 public void set(int index,Book data) { 275 if(index > this.count) { 276 return; 277 } 278 this.foot = 0; 279 this.root.setNode(index, data); 280 } 281 282 /* 283 * 链表数据的删除函数remove 284 * 1.删除之前使用contains函数判断当前数据是否存在 285 * 2.如果存在则判断根节点的数据与当前数据是否相等,如果相等直接删除 286 * 3.如果不相等,则递归调用内部类函数removeNode查找并删除需要删除的节点 287 */ 288 public void remove(Book data) { 289 if(this.contains(data)) { 290 if(data.equals(this.root.data)) { 291 this.root = this.root.next; 292 } 293 else { 294 this.root.next.removeNode(this.root, data); 295 } 296 this.count--; 297 } 298 } 299 300 /* 301 * 将链表中的数据转换为对象数组输出 302 * 303 */ 304 public Book[] toArray() { 305 if(this.root == null) { 306 return null; 307 } 308 this.foot = 0; 309 this.retArray = new Book[this.count]; 310 this.root.toArrayNode(); 311 return this.retArray; 312 } 313 314 /* 315 * 清空链表 316 * 把根节点设置成空 317 * 把count设置为0 318 */ 319 public void clear() { 320 this.root = null; 321 this.count = 0; 322 } 323 324 } 325 //-------------------------节点类结束-------------------------------------- 326 327 public class LinkListDemo{ 328 private static String tushupath = "bookmanage.txt"; 329 330 public void menu() { 331 System.out.println("\t----------------"); 332 System.out.println("\t---1.图书添加----"); 333 System.out.println("\t---2.图书删除----"); 334 System.out.println("\t---3.图书修改----"); 335 System.out.println("\t---4.图书列表----"); 336 System.out.println("\t----------------"); 337 System.out.println("请输入你要选择的功能"); 338 339 } 340 341 342 public static void show() throws IOException { 343 File file = new File(tushupath); 344 Reader in = new FileReader(file); 345 BufferedReader read = new BufferedReader(in); 346 int serial = 1; 347 String str; 348 String headline = "书籍名 价格"; 349 while((str = read.readLine()) != null) { 350 if(str.equals(headline)) { 351 System.out.println("序号" + "\t" + str); 352 } 353 else { 354 System.out.println(serial + "\t" + str); 355 serial++; 356 } 357 358 } 359 } 360 361 public static void bookadd(String bookname,double price) throws IOException{ 362 Link add = new Link(); 363 add.add(new Book(bookname,price)); 364 File file = new File(tushupath); 365 Writer out = new FileWriter(file,true); 366 Book[] books = add.toArray(); 367 for(int x = 0; x < books.length; x++) { 368 out.append(books[x].info()); 369 System.out.println("booktest" + x); 370 } 371 out.close(); 372 } 373 public static void bookremove(String bookname,double price) throws IOException { 374 Link remove = new Link(); 375 remove.remove(new Book(bookname,price)); 376 File file = new File(tushupath); 377 Writer out = new FileWriter(file,true); 378 379 /*Book[] books = remove.toArray(); 380 for(int x = 0; x < books.length; x++) { 381 out.append(books[x].info()); 382 }*/ 383 out.close(); 384 } 385 386 387 388 public static void main(String[] args) throws IOException { 389 String bookname = ""; 390 double price = 0.00; 391 int serial = 0; 392 Scanner scan = new Scanner(System.in); 393 File file = new File(tushupath); 394 Writer out = new FileWriter(file,true); 395 Reader in = new FileReader(file); 396 BufferedReader read = new BufferedReader(in); 397 System.out.println(read.readLine()); 398 String headline = "书籍名\t价格\r\n"; 399 String headline_read = read.readLine(); 400 if(headline.equals(headline_read) ) { 401 out.write(headline); 402 } 403 in.close(); 404 out.close(); 405 406 LinkListDemo list = new LinkListDemo(); 407 int choose = 0; 408 while(true) { 409 list.menu(); 410 choose = scan.nextInt(); 411 if(choose == 1) { 412 System.out.println("你选择了图书添加功能"); 413 System.out.print("请输入你要添加的图书名:"); 414 bookname = scan.next(); 415 System.out.print("请输入你要添加图书的价格:"); 416 price = scan.nextDouble(); 417 LinkListDemo.bookadd(bookname, price); 418 System.out.println("添加成功"); 419 System.out.println("当前图书列表:"); 420 LinkListDemo.show(); 421 } 422 if(choose == 2) { 423 System.out.println("你选择了图书删除功能"); 424 System.out.print("请输入你要删除的图书名:"); 425 bookname = scan.next(); 426 System.out.print("请输入你要删除图书的价格:"); 427 price = scan.nextDouble(); 428 LinkListDemo.bookremove(bookname, price); 429 System.out.println("删除成功"); 430 System.out.println("当前图书列表:"); 431 LinkListDemo.show(); 432 } 433 if(choose == 0) { 434 out.close(); 435 System.exit(0);; 436 } 437 } 438 } 439 }