自定义LinkedList,Object和Set

自定义LinkedList,Object和Set

一,自定义LinkedList代码

package com.qfedu.c_util;
import java.util.NoSuchElementException;
public class MyLinkedList<E> {
 /**
  * 有效元素个数
  */
 private int size = 0;
 /**
  * 第一个Node节点首地址
  */
 private Node<E> first; 
 /**
  * 最后一个Node节点的首地址
  */
 private Node<E> last;
 /**
  * 私有化静态成员内部类
  * 
  * @author Anonymous
  *
  * @param <E> 和MyListedList一直的泛型
  */
 private static class Node<E> {
  /**
   * 在LinkedList中保存的Node节点内元素内容
   */
  E item; 
  /**
   * 下一个Node节点引用,保存下一个节点的空间首地址
   */
  Node<E> next;
  /**
   * 上一个Node节点的,保存上一个节点空间的首地址
   */
  Node<E> prev;  
  /**
   * Node<E>没有无参数构造方法,创建对应Node对象,需要保存前后节点位置,同时需要
   * 包装需要存储的数据,在当前Node节点中
   * 
   * @param prev 前节点位置
   * @param element 存储元素
   * @param next 后节点位置
   */
  public Node(Node<E> prev, E element, Node<E> next) {
   this.item = element;
   this.prev = prev;
   this.next = next;
  }
 } 
 /**
  * 空MyListedList集合,只是准备了一个所谓的链表头
  */
 public MyLinkedList() {}  
 /**
  * 添加一个符合当前MyLinkedList泛型约束数据类型一致的元素到集合中
  * 
  * @param e 符合MyLinkedList 要求的元素
  * @return 添加成功返回true
  */
 public boolean add(E e) {
  // 原本最后一个节点位置,当前元素包装成Node节点之后的存储位置
  Node<E> l = last;  
  // 创建得到一个新的Node节点 
  Node<E> newNode = new Node<>(l, e, null);  
  // 因为当前元素需要保存到LinkedList结尾 last一定要被赋值newNode
  last = newNode;  
  // 第一个有效节点时,first同时指向当前节点
  if (null == first) {
   first = newNode;
  }  else {
   // 原本最后一个节点next指向新的newNode
   l.next = newNode;
  }  
  // 有效元素 += 1
  size += 1;  
  return true;
 } 
 /**
  * 同是尾插法操作
  * 
  * @param e 符合泛型约束的具体数据类型
  */
 public void addLast(E e) {
  add(e);
 } 
 /**
  * 在LinkedList链表头添加一个元素
  * 
  * @param e 符合泛型约束的具体数据类型
  */
 public void addFirst(E e) {
  /*
   * 三个地址:
   *   1. LinkedList链表头 first ==> newNode
   *   2. newNode.next --> old FirstNode
   *   3. old FirstNode.prev --> newNode
   */
  // 原始的first节点
  Node<E> f = first;  
  // 创建新newNodex
  Node<E> newNode = new Node<E>(null, e, f);  
  // LinkedList链表头内first一定执行newNode
  first = newNode;  
  if (null == first) {
   last = newNode;
  } else {
   // 原始的first节点,prev赋值为newNode
   f.prev = newNode;
  }  
  size += 1;
 }
 /**
  * 获取第一个节点元素
  * 
  * @return 第一个Node保存内容
  */
 public E getFirst() {
  Node<E> f = first;  
  if (null == f) {
   throw new NoSuchElementException();
  }  
  return f.item;
 } 
 /**
  * 获取最后一个节点元素
  * 
  * @return 最后一个Node保存内容
  */
 public E getLast() {
  Node<E> l = last;  
  if (null == l) {
   throw new NoSuchElementException();
  }  
  return l.item;
 } 
 /**
  * 或者指定下标(指定计数,第几个节点内存储的元素)
  * @param index 指定的下标位置,计数
  * @return 对应的元素内容
  */
 public E get(int index) {
  if (index < 0 || index >= size) {
   throw new IndexOutOfBoundsException();
  }  
  if (index < (size >> 1)) {
   Node<E> n = first;
   for (int i = 0; i < index; i++) {
    n = n.next;
   }   
   return n.item;
  } else {
   Node<E> n = last;
   for (int i = size - 1; i > index; i--) {
    n = n.prev;
   }   
   return n.item;
  }
 } 
 /**
  * 删除最后一个元素
  * 
  * @return 返回值是保存的元素内容
  */
 public E removeLast() {
  // 最后一个节点
  Node<E> l = last;
  // 最后一个节点的前节点
  Node<E> prev = last.prev;  
  if (null == l) {
   throw new NoSuchElementException();
  }  
  // 原最后一个节点的前节点空间地址赋值为null
  l.prev = null;  
  // last指向原本的最后一个节点的前节点
  last = prev;
  // 前节点为null
  if (null == prev) {
   first = null;
  } else {
   prev.next = null;
  }  
  // 取出节点中保存的数据内容
  E e = l.item;
  // 原最后节点内所有数据全部为null,GC更快的销毁内存
  l.item = null;  
  size -= 1;
  return e;
 } 
 /**
  * 删除链表中的一个节点
  * 
  * @return 被删除节点的保存元素
  */
 public E removeFirst() {
  // 获取第一个节点
  Node<E> f = first;  
  // 如果null == f 证明这是一个空的链表
  if (null == f) {
   throw new NoSuchElementException();
  }  
  // 删除节点之后的节点
  Node<E> next = f.next;
  first = next;  
  if (null == next) {
   last = null;
  } else {
   next.prev = null;
  }  
  // 断开删除节点和原本next节点连接
  f.next = null;
  E e = f.item;
  // JVM的GC效率
  f.item = null;  
  // 有效元素 -= 1
  size -= 1;   
  return e;
 }  
 /**
  * 删除LinkedList中指定元素
  * 
  * @param obj 删除的指定元素
  * @return 删除成功返回true,否则返回false
  */
 public boolean remove(Object obj) {
  Node<E> n = first;
  Node<E> del = null;
  // 1. 利用for循环,以及size有效元素个数,遍历LinkedList
  for (int i = 0; i < size; i++) {
   if (n.item.equals(obj)) {
    // 保留需要删除的节点
    del = n;
    break;
   }   
   n = n.next;
  }  
  return remove(del) != null;
 }  
 /**
  * 删除指定下标的节点
  * 
  * @param index 指定下标位置
  * @return 被删除的元素
  */
 public E remove(int index) {  
  if (index < 0 || index >= size || 0 == size) {
   throw new NoSuchElementException();
  }  
  Node<E> del = null;
  if (index < (size >> 1)) {
   del = first;
   for (int i = 0; i < index; i++) {
    del = del.next;
   }
  } else {
   del = last;
   for (int i = size - 1; i > index; i--) {
    del = del.prev;
   }
  }  
  return remove(del);
 } 
 /**
  * 类内私有化方法,用于删除指定节点
  * @param node 需要删除的节点
  * @return 被删除元素对象, 没有删除返回null
  */
 private E remove(Node<E> node) {
  if (null == node) {
   return null;
  }  
  E e = null;
  if (null == node.prev) {
   return removeFirst();
  } else if (null == node.next) {
   return removeLast();
  } else {
   Node<E> prev = node.prev;
   Node<E> next = node.next;   
   prev.next = next;
   next.prev = prev;
   e = node.item;   
   node.next = null;
   node.prev = null;
   node.item = null;   
   size -= 1; 
   return e;
  }  
 } 
}

1.1 LinkedList特征

1. 存储数据,非连续空间。
2. 数据之间通过引用连接,方便遍历和使用
3. 遍历效率较低,数据非连续空间存储,需要通过引用跳转过程来完成
4. 删除插入操作效率高,但是注意地址的转移和保存问题。
5. LinkedList链表当中的操作其实大部分都是和C语言指针一个概念

二,Object类

2.1 Object类概述

Java中所有类的基类!!!
 Java中所有的类都是间接或者直接继承Object类。
 Object类的引用数据类型变量可以保存Java中任意数据类型空间的首地址。
Object类内规定了一些方法:
 String toString();
  当前对象建议String类型描述。默认情况是当前类所属包名.类名@十六进制内存地址
  如果对于数据类型展示有要求,可以重写toString方法,在展示的方法中会默认执行
  toString方法
 int hashCode();
  内存中当前对象的唯一索引值,默认情况下是当前对象所处空间首地址的十进制展示。
 boolean equals(Object obj);
  比较方法,判断两个对象是否一致,Object类内默认情况下比较的方式是地址比较。
  两个对象地址一致,表示肯定是相同对象。如果我们期望修改equals比较规则,可以
  在当前类内重写
  【注意】
         Java中规定,如果两个对象的equals比较方法结果为true,要求hashCode值必须
         一致 
         
 线程有关方法
 void wait();
 void notify();
 void notifyAll(); 
 
 反射有关方法
 Class<?> getClass();

2.2 toString方法

食之无味,弃之可惜!!!
	 目前大家展示数据时,需要考虑使用的方法,可以通过Sout方法直接展示出对应的对象内容。		 
使用DEBUG工具,一些辅助的可视化工具使用。
Eclipse Alt + Shift + S

2.3 equals方法

 比较两个对象是否一致,在Object类内默认方式是比较两个对象的地址是否一致。
 代码中存在一些情况,需要比较的是两个对象中保存的内容是一直,但是使用Object类内继承而来的equals方法,是不合理的!!!
 
 【实现】
  这里需要重写equals方法
 /*                                                                   
 * 重写equals方法                                                        
 *   1. 判断两个对象是不是同一个对象。如果调用方法的类对象和传入参数类对象    
 *   地址一致,那就是同一个对象,返回true,搞定!!!         
 *                                                                   
 *   2. equals方法参数是Object类型,那也就是说任何类型的数据都可以作为参数。   
 *   两个数据类型不一致,是否需要进行比较操作。                               
 *   判断数据类型是否一致                                                   
 *   使用关键字 instanceOf,同数据类型继续运行,非同类型,结束判断返回false   
 *   格式:                                                          
 *    类对象 instanceOf 类名                                        
 *                                                                   
 *   3. 判断对象中保存的数据                                                
 *    Student中我们比较id, name, age, gender就可以了                    
 *                                                                
 */                                                                  
@Override                                                            
public boolean equals(Object obj) {                                  
 // 1. 判断是不是同地址对象                                                 
 if (this == obj) {                                               
  return true;                                                 
 }                       
 // 2. 类型是否一致                                                     
 if (!(obj instanceof Student)) {                                 
  return false;                                                
 }                                                    
 // 数据类型强制转换                                                      
 Student stu = (Student) obj;                                     
 return this.id == stu.id                                         
   // this.name.equals(stu.name) 该equals方法是String类equals方法  
   && this.name.equals(stu.name)                            
   && this.age == stu.age                                   
   && this.gender == stu.gender;                                      
}   

2.4 hashCode方法

 在Object类内,hashCode方法,返回的内容是当前对象的空间首地址十进制展示方式。 
 当前类重写equals方法之后,两个当前类对象比较结果为true,那么要求这两个对象的hashCode必须一致!!!
 hashCode使用有一个唯一原则。
 一般会参考参与equals比较的所有成员变量来组成对应的hashCode,这里会使用到一些Java中提供的计算哈希值的方法。 
 hashCode使用未进行重写的情况下,会使用地址作为hashCode对应的数据,重写之后,不再使用地址。重写之后hashCode 不对应当前对象所在地址。
 
@Override
	public int hashCode() {
	 // 这里通过Objects 工具类内的hash方法,传入所有参与equals比较的成员变量
	 // 得到对应的hashCode值
	 return Objects.hash(id, name, age, gender);
	}

三,Set集合

3.1 Set集合概述

特征:
 无序,不可重复
 无序:添加顺序和存储顺序不一致,【不代表有排序效果】
 不可重复: 在一个Set集合中不能出现相同元素
interface Set<E> 
--| class HashSet<E> 底层是哈希表存储数据
--| class TreeSet<E> 底层存储数据是一个二叉树!

3.2 HashSet底层结构

在这里插入图片描述

3.3 Tree树形结构

TreeSet存储方式:没有比较方式无法存储

在这里插入图片描述

3.4 Comparable接口使用

interface Comparable<T> {
         int compareTo(T t);
}
方法参数为T类型,由实现类遵从接口时约束,
 compareTo方法,返回值类型int类型,0, 负数,正数
 0 表示两个元素一致,如果在TreeSet中比较结果为0,表示同一个元素,无法存储第二个。
	Comparable接口由存储元素对应的类遵从,完成该方法

3.5 Comparator接口使用

interface Comparator<T> {
         int compare(T o1, T o2);
}
需要完成一个自定义比较器类对象,
 int 返回值 0,负数,正数
 0 表示两个元素一致,如果在TreeSet中比较结果为0,表示同一个元素,无法存储第二个。		

Comparator使用要高于Comparable使用

发布了6 篇原创文章 · 获赞 0 · 访问量 256

猜你喜欢

转载自blog.csdn.net/qq_39544980/article/details/104485024
今日推荐