容器【容器介绍、Set接口介绍、 HashSet容器的使用、TreeSet容器的使用】(三)-全面详解(学习总结---从入门到深化)

2023-07-11 22:05:20

目录

LinkedList容器介绍

Set接口介绍

 HashSet容器的使用

 通过HashSet存储自定义对象

TreeSet容器的使用


LinkedList容器介绍

LinkedList底层用双向链表实现的存储。特点:查询效率低,增删效率高,线程不安全。 双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两 个指针,分别指向前一个节点和后一个节点。 所以,从双向链表中 的任意一个节点开始,都可以很方便地找到所有节点。

 LinkedList的存储结构图

 每个节点都应该有3部分内容:

class  Node<E> {
    Node<E>  previous;   //前一个节点
    E  element;          //本节点保存的数据
    Node<E> next;        //后一个节点
}

List实现类的选用规则

如何选用ArrayList、LinkedList、Vector?

1 需要线程安全时,用Vector。

2 不存在线程安全问题时,并且查找较多用ArrayList(一般使用它)

3 不存在线程安全问题时,增加或删除元素较多用LinkedList

LinkedList容器的使用(List标准) 

LinkedList实现了List接口,所以LinkedList是具备List的存储特征的 (有序,元素有重复)。

public class LinkedListTest {
    public static void main(String[] args) {
        //实例化LinkedList容器
        List<String> list = new LinkedList<>();
        //添加元素
        boolean a = list.add("a");
        boolean b = list.add("b");
        boolean c = list.add("c");
        list.add(3,"a");
        System.out.println(a+"\t"+b+"\t"+c);
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
       }
   }
}

LinkedList容器的使用(非List标准)

public class LinkedListTest2 {
    public static void main(String[] args) {
        System.out.println("-------LinkedList-------------");
        //将指定元素插入到链表开头
        LinkedList<String> linkedList1 = new LinkedList<>();
        linkedList1.addFirst("a");
        linkedList1.addFirst("b");
        linkedList1.addFirst("c");
        for (String str:linkedList1){
            System.out.println(str);
       }
        System.out.println("----------------------");
        //将指定元素插入到链表结尾
        LinkedList<String> linkedList = new LinkedList<>();
        linkedList.addLast("a");
        linkedList.addLast("b");
        linkedList.addLast("c");
        for (String str:linkedList){
            System.out.println(str);
       }
        System.out.println("---------------------------");
        //返回此链表的第一个元素
      
        System.out.println(linkedList.getFirst());
        //返回此链表的最后一个元素
      
        System.out.println(linkedList.getLast());
        System.out.println("-----------------------");
        //移除此链表中的第一个元素,并返回这个元素
        linkedList.removeFirst();
        //移除此链表中的最后一个元素,并返回这个元素
        linkedList.removeLast();
        for (String str:linkedList){
            System.out.println(str);
       }
        System.out.println("-----------------------");
        linkedList.addLast("c");
        //从此链表所表示的堆栈处弹出一个元素,等效于removeFirst
        linkedList.pop();
        for (String str:linkedList){
            System.out.println(str);
       }
        System.out.println("-------------------");
        //将元素推入此链表所表示的堆栈 这个等效于addFisrt(E e)
        linkedList.push("h");
        for (String str:linkedList){
            System.out.println(str);
       }
   }
}
        

LinkedList的源码分析

添加元素

private static class Node<E> {
    E item;
    Node<E> next;
    Node<E> prev;
    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
   }
}

成员变量

transient int size = 0;

/**
* Pointer to first node.
* Invariant: (first == null && last == null) ||
*           (first.prev == null && first.item != null)
*/

transient Node<E> first;

/**
* Pointer to last node.
* Invariant: (first == null && last == null) ||
*            (last.next == null && last.item != null)
*/

transient Node<E> last;

添加元素

/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
* @return {@code true} (as specified by {@link Collection#add})
*/

public boolean add(E e) {
    linkLast(e);
    return true;
}

/**
* Links e as last element.
*/
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
        first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

头尾添加元素

addFirst

/**
* Inserts the specified element at the beginning of this list.
*
* @param e the element to add
*/

public void addFirst(E e) {
    linkFirst(e);
}

/**
* Links e as first element.
*/
private void linkFirst(E e) {
    final Node<E> f = first;
    final Node<E> newNode = new Node<>(null, e, f);
    first = newNode;
    if (f == null)
    last = newNode;
    else
        f.prev = newNode;
    size++;
    modCount++;
}

addLast

/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/

public void addLast(E e) {
    linkLast(e);
}

/**
* Links e as last element.
*/
void linkLast(E e) {
    final Node<E> l = last;
    final Node<E> newNode = new Node<>(l, e, null);
    last = newNode;
    if (l == null)
    first = newNode;
    else
        l.next = newNode;
    size++;
    modCount++;
}

获取元素

/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/

public E get(int index) {
    checkElementIndex(index);
    return node(index).item;
}
private void checkElementIndex(int index) {
    if (!isElementIndex(index))
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
/**
* Tells if the argument is the index of an existing element.
*/

private boolean isElementIndex(int index) {
    return index >= 0 && index < size;
}   
/**
* Returns the (non-null) Node at the specified element index.
*/
Node<E> node(int index) {
    // assert isElementIndex(index);
    if (index < (size >> 1)) {
        Node<E> x = first;
        for (int i = 0; i < index; i++)
            x = x.next;
        return x;
   } else {
        Node<E> x = last;
        for (int i = size - 1; i > index; i--)
            x = x.prev;
            return x;
   }
}

Set接口介绍

 Set接口继承自Collection接口,Set接口中没有新增方法,它和 Collection接口保持完全一致。我们在前面学习List接口的使用方 式,在Set中仍然适用。因此,学习Set的使用将没有任何难度。

 Set接口特点

Set特点:无序、不可重复。无序指Set中的元素没有索引,我们只 能遍历查找;不可重复指不允许加入重复的元素。更确切地讲,新 元素如果和Set中某个元素通过equals()方法对比为true,则只能保 留一个。

 Set常用的实现类有:HashSet、TreeSet等,我们一般使用 HashSet。

 HashSet容器的使用

HashSet是Set接口的实现类。是Set存储特征的具体实现。

public class HashSetTest {
    public static void main(String[] args) {
        //实例化HashSet
        Set<String> set = new HashSet<>();
        //添加元素
        set.add("a");
        set.add("b1");
        set.add("c2");
        set.add("d");
        set.add("a");
        //获取元素,在Set容器中没有索引,所以没有对应的get(int index)方法
        for(String str: set){
            System.out.println(str);
       }
        System.out.println("--------------------");
        //删除元素
        boolean flag = set.remove("c2");
        System.out.println(flag);
        for(String str: set){
            System.out.println(str);
       }
        System.out.println("------------------------");
        int size = set.size();
        System.out.println(size);
   }
}

HashSet存储特征分析

 HashSet 是一个不保证元素的顺序且没有重复元素的集合,是线程 不安全的。HashSet允许有null 元素。

 无序:

在HashSet中底层是使用HashMap存储元素的。HashMap底层使 用的是数组与链表实现元素的存储。元素在数组中存放时,并不是 有序存放的也不是随机存放的,而是对元素的哈希值进行运算决定 元素在数组中的位置。

 不重复:

当两个元素的哈希值进行运算后得到相同的在数组中的位置时,会 调用元素的equals()方法判断两个元素是否相同。如果元素相同则 不会添加该元素,如果不相同则会使用单向链表保存该元素。

 通过HashSet存储自定义对象

创建Users对象

public class Users {
    private String username;
    private int userage;
    public Users(String username, int userage) {

    this.username = username;
        this.userage = userage;
   }
    public Users() { }
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Users users = (Users) o;
        if (userage != users.userage) return false;
        return username != null ? username.equals(users.username) : users.username == null;
   }

    @Override
    public int hashCode() {
        int result = username != null ? username.hashCode() : 0;
        result = 31 * result + userage;
        return result;
       }
    public String getUsername() {
        return username;
      }
    public void setUsername(String username)
      {
        this.username = username;
      }
    public int getUserage() {
        return userage;
      }
    public void setUserage(int userage) {
        this.userage = userage;
      }
    @Override
    public String toString() {
        return "Users{" +
                "username='" + username + '\'' +
                ", userage=" + userage +
               '}';
   }
}

 在HashSet中存储Users对象

public class HashSetTest2 {
    public static void main(String[] args) {
        //实例化HashSet
        Set<Users> set = new HashSet<>();
        Users u = new Users("oldlu",18);
        Users u1 = new Users("oldlu",18);
        set.add(u);
        set.add(u1);
        System.out.println(u.hashCode());
        System.out.println(u1.hashCode());
        for(Users users:set){
            System.out.println(users);
       }
   }
}

HashSet底层源码分析

成员变量

private transient HashMap<E,Object> map;
// Dummy value to associate with an Object in the backing Map
private static final Object PRESENT = new Object();

添加元素

/**
* Adds the specified element to this set if it is not already present.
* More formally, adds the specified element <tt>e</tt> to this set if
* this set contains no element <tt>e2</tt> such that
* <tt>(e==null&nbsp;? &nbsp;e2==null&nbsp;:&nbsp;e.equals(e2)) </tt>.
* If this set already contains the element, the call leaves the set
* unchanged and returns <tt>false</tt>.
*
* @param e element to be added to this set
* @return <tt>true</tt> if this set did not already contain the specified
* element
*/
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}

TreeSet容器的使用

 TreeSet实现了Set接口,它是一个可以对元素进行排序的容器。底 层实际是用TreeMap实现的,内部维持了一个简化版的TreeMap, 通过key来存储元素。 TreeSet内部需要对存储的元素进行排序,因 此,我们需要给定排序规则。

排序规则实现方式:

1、通过元素自身实现比较规则。

2、通过比较器指定比较规则。

public class TreeSetTest {
    public static void main(String[] args) {
        //实例化TreeSet
        Set<String> set = new TreeSet<>();
        //添加元素
        set.add("c");
        set.add("a");
        set.add("d");
        set.add("b");
        set.add("a");
        //获取元素
        for(String str :set){
            System.out.println(str);
       }
   }
}
更多推荐

Vue模板语法(下)

一.事件处理器什么是事件处理器建立一个HTML编写事件处理器测试结果二.表单的综合案例什么是表单综合案例建立一个HTML来编写表单案例测试结果三.局部组件什么是组件通信自定义组件测试结果组件通信-父传子测试结果组件通信-子传父测试结果一.事件处理器什么是事件处理器事件处理器是一种用于响应和处理用户交互事件的机制。在We

zookeeper最基础教程

文章目录一、简介1、工作机制2、特点3、数据结构4、应用场景5、选举机制二、软件安装1、单机版安装2、集群安装3、配置参数解读(zoo.cfg)4、ZK集群启动脚本三、命令行操作1、语法2、使用3、节点相关4、监听器原理5、节点删除与查看三、写数据流程一、简介1、工作机制官方地址:https://zookeeper.a

SpringMVC之JSON数据返回与异常处理机制---全方面讲解

一,JSON数据返回的理解在SpringMVC中,当需要将数据以JSON格式返回给客户端时,可以使用@ResponseBody注解或@RestController注解将Controller方法的返回值直接转化为JSON格式并返回。这使得开发者可以方便地将Java对象转换为JSON,并通过HTTP响应返回给客户端。Spr

TCP协议

文章目录TCP协议1.TCP协议段格式(1)2个核心问题(解包与分用)(2)TCP的可靠性2.确认应答(ACK)机制3.16位窗口大小4.6位标志位(1)ACK(2)SYN(3)FIN(4)PSH(5)RST(6)URG5.超时重传机制6.连接管理机制6.0TCP协议通讯流程6.1三次握手(1)前置问题(2)细节问题(

面试题三:请你谈一谈Vue中的filter功能的实现

Vue中过滤器(filter)的使用我们想一下有methods为什么要有filter的存在呢,因为filter的实现效率比methods要高的多。看一下官方定义:Vue.js允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和v-bind表达式(后者从2.1.0+开始支持)。过滤器应

Vue3 学习-组件通讯(二)

文章目录前言一、props通信二、自定义事件(emit)三、全局事件总线(EventBus)四、v-model五、userAttrs六、ref和$parent七、Provide与Inject八、pinia九、slot插槽总结前言本文主要记录Vue3的九种组件通信方式一、props通信子组件需要用defineProps方

springboot日志配置(logback+slf4j配置)

1.为什么要配置日志故障排查和问题分析:日志记录允许开发人员和运维人员在系统发生问题或故障时追踪问题的根本原因。通过查看日志文件,他们可以了解系统在特定时间点发生了什么事情,从而更容易定位和解决问题。性能监控和优化:日志记录可以帮助监控应用程序和系统的性能。通过分析日志数据,你可以识别性能瓶颈和瓶颈的位置,从而采取相应

RPA和传统自动化的区别?

随着科技的不断发展,企业对于自动化工具的需求也在逐渐增加。在自动化领域中,传统的自动化技术和现代的RPA(RoboticProcessAutomation)都是重要的自动化工具,但它们之间存在一些明显的区别。本文将从技术、应用和未来趋势三个方面来探讨RPA和传统自动化的区别。一、技术上的区别自动化方式传统的自动化技术通

Java安全入门笔记(持续更新)

之前陆陆续续学过一点Java安全,笔记一直都没没有系统的写过,现在重新深入学一下之前的知识,会把笔记持续更新过来Java反射反射是java得一个重要特性,它可以获取一个类的所有信息,还可以执行类中的方法反射赋予Java动态特性我个人感觉静态语言的安全性是比较高的,因为一个供给使用的静态语言的程序的结构时固定的,能给攻击

Linux内核源码分析 (B.11) 从内核世界透视 mmap 内存映射的本质(原理篇)

Linux内核源码分析(B.11)从内核世界透视mmap内存映射的本质(原理篇)文章目录Linux内核源码分析(B.11)从内核世界透视mmap内存映射的本质(原理篇)1\.详解内存映射系统调用mmap2\.私有匿名映射3\.私有文件映射4\.共享文件映射5\.共享匿名映射6\.参数flags的其他枚举值7\.大页内存

说说Object类下面有几种方法呢?

今天说一道基础题型,不过很多人会忽略或者至少说不完整,但是面试时被问到的几率还是很大的。面试题Object有几种方法呢?Java语言是一种单继承结构语言,Java中所有的类都有一个共同的祖先。这个祖先就是Object类。如果一个类没有用extends明确指出继承于某个类,那么它默认继承Object类。Object的方法

热文推荐