Java集合之LinkedList详解

news2024/11/16 7:44:24

Java集合之LinkedList

  • 一、LinkedList类的继承关系
    • 1. 基类功能说明
      • 1.1. Iterator:提供了一种方便、安全、高效的遍历方式。
      • 1.2. Collection:为了使ArrayList具有集合的基本特性和操作。
      • 1.3. Queue: LinkedList是一种队列(Queue)数据结构的实现。
      • 1.4. List:为了让其具有列表的特性和更多的操作。
      • 1.5. AbstractCollection:提供了一些通用的集合操作。
      • 1.6. Deque: 使得LinkedList可以满足更多的使用场景,例如实现双端队列、实现栈等数据结构。
      • 1.7. AbstractList:为了使其具有列表的抽象特性和操作。
      • 1.8. AbstractSequentialList:可以更方便地实现List接口中的方法,同时也提供了一些保护方法,供LinkedList实现自己特有的方法。
      • 1.9. Cloneable:支持克隆操作。
      • 10. Serializable:支持序列化。
  • 二、LinkedList的优缺点
    • 1. 优点:
      • 1.1. 高效的插入和删除操作
      • 1.2. 不需要预先分配空间
      • 1.3. 可以作为双端队列使用
    • 2. 缺点:
      • 2.1. 随机访问性能较差
      • 2.2. 占用更多的内存空间
      • 2.3. 不支持随机访问
    • 3. 总结
  • 三、LinkedList数据结构
  • 四、LinkedList源码分析
    • 1. 基本属性
      • 1.1 size :节点个数
      • 1.2 first:头结点
      • 1.3 last:尾结点
    • 2. 构造方法
    • 3. add方法
    • 4. set方法
    • 5. get方法
    • 6. remove方法
    • 7. clear方法
    • 8. node(index)方法
    • 9. unlink方法
    • 10. linkBefore方法
      • 助解图:
    • 11. linkLast方法
  • 五、常见遍历方式
    • LinkedList有以下4种常见的遍历方式:
      • 1、使用for循环遍历:
      • 2、使用foreach循环遍历:
      • 3、使用Iterator遍历:
      • 4、使用ListIterator遍历:

一、LinkedList类的继承关系

LinkedList

1. 基类功能说明

1.1. Iterator:提供了一种方便、安全、高效的遍历方式。

  1. Iterator是一个迭代器接口,它提供了一种安全的遍历集合元素的方法,可以避免在遍历过程中修改集合引起的ConcurrentModificationException异常,同时还可以避免在遍历过程中删除集合元素时出现索引越界等问题。

  2. LinkedList使用Iterator遍历集合时,可以使用hasNext()方法判断是否还有元素,使用next()方法获取下一个元素,使用remove()方法安全地删除当前元素。因此,使用Iterator遍历LinkedList既方便又安全,是一种非常推荐的遍历方式。

  3. 另外,Iterator接口是Java集合框架中的一部分,实现Iterator接口可以使LinkedList更加符合集合框架的统一标准,方便与其他集合类一起使用。

1.2. Collection:为了使ArrayList具有集合的基本特性和操作。

  1. LinkedList是Java中常用的集合类之一,它实现了Collection接口,这是为了使LinkedList具有集合的基本特性和操作,例如添加、删除、遍历等。
  2. 实现Collection接口可以让LinkedList能够和其他Java集合类兼容,可以方便地进行集合之间的转换和使用。同时,实现Collection接口也使得LinkedList能够支持泛型,可以在编译期间进行类型检查,避免了运行时出现类型错误的问题。
  3. 总之,LinkedList实现Collection接口是为了使其具有更多的特性和更好的兼容性。

1.3. Queue: LinkedList是一种队列(Queue)数据结构的实现。

  1. Queue接口定义了一些基本的队列操作,例如添加元素、删除元素、查看队首元素等,LinkedList实现了这些操作,因此可以被视为一种队列数据结构。

1.4. List:为了让其具有列表的特性和更多的操作。

  1. ArrayList实现了List接口,是为了使其具有列表的特性和操作,例如按索引访问元素、插入、删除、替换等。List是Collection接口的子接口,提供了更多针对列表的操作。
  2. 例如按索引操作、排序、子列表等。因此,实现List接口可以让ArrayList具有更多的操作和更好的兼容性,可以和其他实现List接口的Java集合类进行交互和转换。

1.5. AbstractCollection:提供了一些通用的集合操作。

  1. 例如containsAll、removeAll、retainAll等方法,这些方法可以被ArrayList直接继承和使用,从而避免了重复实现这些操作。
  2. 同时,AbstractCollection也提供了一些抽象方法,例如size、iterator、toArray等方法,这些方法需要由具体的集合类去实现,LinkedList也需要实现这些方法以完成自身的功能。

1.6. Deque: 使得LinkedList可以满足更多的使用场景,例如实现双端队列、实现栈等数据结构。

  1. 这使得LinkedList可以满足更多的使用场景,例如实现双端队列、实现栈等数据结构。同时,LinkedList还可以在队首和队尾进行高效的插入和删除操作,因为它的底层实现是双向链表,而双向链表可以在常数时间内进行插入和删除操作。
  2. 因此,LinkedList继承Deque接口的作用在于让LinkedList具备了双端队列的功能,同时也提高了LinkedList的灵活性和可用性。

1.7. AbstractList:为了使其具有列表的抽象特性和操作。

  1. AbstractList是List接口的一个抽象实现,实现了List接口的大部分方法,包括添加、删除、获取元素、遍历等。

  2. LinkedList作为List接口的一个具体实现,需要实现List接口中定义的所有方法。

  3. 通过继承AbstractList抽象类,LinkedList可以重用AbstractList中已经实现的方法,减少重复代码,提高代码的复用性和可维护性。

  4. 另外,AbstractList还提供了一些抽象方法,例如get()、set()、add()、remove()等,这些抽象方法需要LinkedList子类实现,从而使得LinkedList具备列表的基本操作。

  5. AbstractList还提供了一些模板方法,例如addAll()、removeAll()等,这些方法可以通过调用抽象方法实现具体的操作。通过继承AbstractList抽象类,LinkedList可以利用这些模板方法快速实现各种列表操作,提高了开发效率。

  6. 综上所述,LinkedList实现了AbstractList抽象类是为了重用AbstractList中已经实现的方法,提高代码的复用性和可维护性,同时也能够利用AbstractList中提供的模板方法快速实现各种列表操作。

1.8. AbstractSequentialList:可以更方便地实现List接口中的方法,同时也提供了一些保护方法,供LinkedList实现自己特有的方法。

  1. AbstractSequentialList是一个抽象类,它实现了List接口中大部分方法,例如add、remove、get、set等。而这些方法的实现都是基于迭代器(Iterator)进行的,迭代器是用于遍历集合中元素的一个对象。因此,继承AbstractSequentialList可以让LinkedList更容易地实现List接口中的这些方法,只需要实现迭代器即可。

  2. 另外,AbstractSequentialList还提供了一些保护方法(protected method),例如getIterator、getListIterator等,可以供子类使用。LinkedList继承了AbstractSequentialList后,可以使用这些保护方法来实现自己特有的方法。

  3. 总之,LinkedList继承AbstractSequentialList的作用在于可以更方便地实现List接口中的方法,同时也提供了一些保护方法,供LinkedList实现自己特有的方法。

1.9. Cloneable:支持克隆操作。

  1. Cloneable接口是一个标记接口,用于表示实现该接口的对象可以进行克隆操作。

  2. 在LinkedList中,克隆操作可以用于创建一个与原列表相同的新列表,这个新列表与原列表相互独立,对新列表的修改不会影响到原列表。这在某些场景下非常有用,例如需要对一个列表进行操作,但是又需要保留原列表不变的情况下,可以先克隆出一个新列表进行操作。

  3. 需要注意的是,LinkedList实现Cloneable接口只是表示它支持克隆操作,并不代表它的克隆操作一定是完全正确和安全的。在进行克隆操作时,需要注意可能存在的浅拷贝和深拷贝问题,以及可能会影响到对象的不变性和线程安全性问题等。

  4. 因此,在使用LinkedList的克隆操作时,需要仔细考虑其对程序的影响,并进行必要的安全性和正确性检查。

10. Serializable:支持序列化。

  1. 序列化是将对象转换为字节流的过程,可以用于持久化对象、网络传输等操作。实现Serializable接口可以让LinkedList的实例对象被序列化,以便于在需要的时候进行序列化操作。

二、LinkedList的优缺点

1. 优点:

1.1. 高效的插入和删除操作

  1. 由于LinkedList底层实现是双向链表,因此在插入和删除操作方面具有高效性。在链表的任意位置插入或删除一个元素的时间复杂度是O(1),而在数组中插入或删除元素的时间复杂度是O(n)。

1.2. 不需要预先分配空间

  1. 由于LinkedList是动态的数据结构,因此在使用时不需要预先分配空间,可以根据需要动态地添加或删除元素,使得空间利用率更高。

1.3. 可以作为双端队列使用

  1. 由于LinkedList实现了Deque接口,因此可以在队首和队尾进行高效的插入和删除操作,使得LinkedList可以作为双端队列使用。

2. 缺点:

2.1. 随机访问性能较差

  1. 由于LinkedList底层实现是链表,因此在访问某个元素时需要从头开始遍历整个链表,时间复杂度为O(n)。而在数组中,访问某个元素的时间复杂度为O(1)。

2.2. 占用更多的内存空间

  1. 由于LinkedList需要存储指向下一个元素和上一个元素的指针,因此占用的内存空间相对于数组要更多。

2.3. 不支持随机访问

  1. 由于LinkedList是基于链表实现的,因此不支持随机访问,只能通过遍历整个链表来访问元素。这也使得LinkedList在某些场景下性能较差,例如在对元素进行排序、查找等操作时。

3. 总结

  1. 总之,LinkedList的优点在于高效的插入和删除操作、不需要预先分配空间、可以作为双端队列使用;缺点在于随机访问性能较差、占用更多的内存空间、不支持随机访问。因此,在使用LinkedList时需要根据具体的场景进行选择。

三、LinkedList数据结构

数据结构

四、LinkedList源码分析

1. 基本属性

1.1 size :节点个数

transient int size = 0;

1.2 first:头结点

transient Node<E> last;

1.3 last:尾结点

transient Node<E> last;

2. 构造方法

public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
  1. 无参构造方法:因为底层是双向链表,所以无需指定初始长度。
  2. 有参构造方法:创建一个包含指定Collection中所有元素的LinkedList对象。
  3. 这两个构造方法的使用场景如下:
    3.1 无参构造方法可以用于创建一个空的LinkedList对象,然后通过add()方法逐个添加元素。
    3.2 带有Collection参数的构造方法可以用于创建一个包含指定元素的LinkedList对象,例如:
List<Integer> list = Arrays.asList(1, 2, 3);
LinkedList<Integer> linkedList = new LinkedList<>(list);
  1. 总之,LinkedList的两个构造方法分别用于创建一个空的LinkedList对象和创建一个包含指定元素的LinkedList对象。通过这两个构造方法,我们可以实现LinkedList的快速创建和初始化。

3. add方法

public boolean add(E e) {
	linkLast(e);
	return true;
}
public void add(int index, E element) {
	checkPositionIndex(index);
	if (index == size)
	    linkLast(element);
	else
	    linkBefore(element, node(index));
}

add(E e) :调用linkLast方法将当前元素添加到链表尾部。(linkLast见下文)
add(int index, E element):

  1. 调用checkPositionIndex校验下标是否越界(index >= 0 && index <= size)。
  2. 如果要添加的元素下标位于链表的最后一个,则调用linkLast方法追加到链表尾部。
  3. 否则不是最后一个,调用linkBefore方法将元素插入指定位置。(linkBefore见下文)。

4. set方法

public E set(int index, E element) {
    checkElementIndex(index);
    Node<E> x = node(index);
    E oldVal = x.item;
    x.item = element;
    return oldVal;
}
  1. 调用checkPositionIndex校验下标是否越界(index >= 0 && index <= size)
  2. 调用node(index) (见下文)
  3. 获取要插入结点的原值oldVal
  4. 将当前元素设置找到的结点位置
  5. 返回结点的原值(oldVal )

5. get方法

public E get(int index) {
	 checkElementIndex(index);
	 return node(index).item;
}
public E getFirst() {
	 final Node<E> f = first;
	 if (f == null)
	     throw new NoSuchElementException();
	 return f.item;
}

get(int index):

  1. 调用checkPositionIndex校验下标是否越界(index >= 0 && index <= size)
  2. 调用node(index) (该方法见下文)
    getFirst():
  3. 返回链表的头结点

6. remove方法

public boolean remove(Object o) {
	 if (o == null) {
	     for (Node<E> x = first; x != null; x = x.next) {
	         if (x.item == null) {
	             unlink(x);
	             return true;
	         }
	     }
	 } else {
	     for (Node<E> x = first; x != null; x = x.next) {
	         if (o.equals(x.item)) {
	             unlink(x);
	             return true;
	         }
	     }
	 }
 return false;
}
public E remove(int index) {
	 checkElementIndex(index);
	 return unlink(node(index));
}

remove(int index):

  1. 调用checkPositionIndex校验下标是否越界(index >= 0 && index <= size)
  2. 调用node(index) (见下文)
  3. 调用unlink方法,移除index位置的节点(unlink方见下文)

remove(Object o) :

  1. 如果 o == null 从链表的第一个结点first开始遍历,寻找结点item属性为null的结点,调用unlink()方法将该节点移除。
  2. 如果 o 不等于 null 从链表的第一个结点first开始遍历,寻找item属性与之相同的为null的结点,调用unlink()方法将该节点移除。

7. clear方法

public void clear() {
    // Clearing all of the links between nodes is "unnecessary", but:
    // - helps a generational GC if the discarded nodes inhabit
    //   more than one generation
    // - is sure to free memory even if there is a reachable Iterator
    for (Node<E> x = first; x != null; ) {
        Node<E> next = x.next;
        x.item = null;
        x.next = null;
        x.prev = null;
        x = next;
    }
    first = last = null;
    size = 0;
    modCount++;
}
  1. 从头结点first开始遍历,将所有属性item、next、prev置为null。
  2. 将头尾结点置为null。
  3. 将size属性设置为0。
  4. 将modCount++,在这个过程中,LinkedList的结构发生了变化,因此modCount需要+1。

8. node(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;
    }
}
  1. 根据下标调用node(index)找到目标结点
  2. 判断当前下标位于链表的左侧还是右侧(size >> 1)
  3. 如果是左侧从first结点开始遍历
  4. 否则从链表的尾部last结点往前遍历,根据prev指针遍历

9. unlink方法

E unlink(Node<E> x) {//移除链表上的x结点
    // assert x != null;
    final E element = x.item;
    final Node<E> next = x.next;
    final Node<E> prev = x.prev;

    if (prev == null) {
        first = next;
    } else {
        prev.next = next;
        x.prev = null;
    }

    if (next == null) {
        last = prev;
    } else {
        next.prev = prev;
        x.next = null;
    }

    x.item = null;
    size--;
    modCount++;
    return element;
}
  1. 分别获取当前结点的ele元素、前驱结点prev、后继结点next
  2. 如果prev等于null,则头结点first指向next,否则将prev的下一个结点指向当前结点的下一个结点。
  3. 如果next等于null,将last指向当前要删除结点的前一个结点,也即prev。否则将next的前驱结点指向要删除的前驱结点prev,同时将当前要删除的结点的next指向为null
  4. 将当前结点的item元素置为null
  5. size元素数量个数-1
  6. modCount+1,因为链表结构发生改变了

10. linkBefore方法

//将元素e插入succ结点的前面
void linkBefore(E e, Node<E> succ) {
    // assert succ != null;
    final Node<E> pred = succ.prev;
    final Node<E> newNode = new Node<>(pred, e, succ);
    succ.prev = newNode;
    if (pred == null)
        first = newNode;
    else
        pred.next = newNode;
    size++;
    modCount++;
}
  1. 定义succ的前驱结点pred
  2. 构造一个新的结点newNode pred就是前驱结点、e当前元素、succ做为当前newNode 的下一个结点。
  3. 修改succ结点的前驱结点为newNode
  4. 如果pred == null 说明链表中只有一个结点succ,则将newNode 作为头结点,将first指向newNode
  5. 否则pred 不等于null,修改pred的后续结点指向,将pred.next 指向刚刚构造出来的新结点newNode
  6. 将size+1
  7. 修改modCount

助解图:

linkbefore

11. linkLast方法

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++;
}
  1. 将变量 l 指向链表的最后一个结点last(暂存)。
  2. 把传进来的元素e,构造一个新的结点newNode。
  3. 将last引用指向newNode。
  4. 如果l == null,说明链表中没有元素,将first指向newNode,做为头结点。
  5. 否则l 不等于null,将 newNode 追加为链表的最后一个元素。
  6. 将链表的size元素数量+1,modCount+1,用来实现快速失败机制。

五、常见遍历方式

LinkedList有以下4种常见的遍历方式:

1、使用for循环遍历:

可以使用for循环和get方法,从头到尾遍历LinkedList中的元素。

for (int i = 0; i < linkedList.size(); i++) {
    E element = linkedList.get(i);
    // 处理当前元素
}

2、使用foreach循环遍历:

可以使用foreach循环,从头到尾遍历LinkedList中的元素。

for (E element : linkedList) {
    // 处理当前元素
}

3、使用Iterator遍历:

可以使用Iterator,从头到尾遍历LinkedList中的元素。

Iterator<E> iterator = linkedList.iterator();
while (iterator.hasNext()) {
    E element = iterator.next();
    // 处理当前元素
}

4、使用ListIterator遍历:

可以使用ListIterator,从头到尾遍历LinkedList中的元素,还可以在遍历时进行添加、删除、修改等操作。

ListIterator<E> listIterator = linkedList.listIterator();
while (listIterator.hasNext()) {
    E element = listIterator.next();
    // 处理当前元素
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/682930.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

I/O error on POST request for “...“ PKIX path building failed的解决办法

异常&#xff1a; 项目中需要用RestTemplate调三方接口&#xff0c;url是https开头加密的。postman可以调通&#xff0c;代码提示没有证书&#xff0c;具体如下&#xff1a; [ERROR][2023-06-25 10:41:16,574][com.peraglobal.restInterface.controller.PLMController]I/O err…

MySQL如何在Centos7环境安装:简易指南

目录 前言 一、卸载不要的环境 1.检查本地MySQL是否正在运行 2.停止正在运行的MySQL 二、检查系统安装包 三、卸载这些默认安装包 1.手动一个一个卸载 2.自动卸载全部 四、获取mysql官方yum源 五、安装mysql yum源&#xff0c;对比前后yum源 1.安装前 2.安装中 3.…

MySQL进阶SQL语句之函数运用

目录 1.select&#xff08;显示表格中一个或数个字段的所有数据记录&#xff09; 2.distinct&#xff08;不显示重复的数据记录&#xff09; 3.where&#xff08;有条件查询&#xff09; 4.and 、or&#xff08;且、或&#xff09; 5. in&#xff08;显示已知的值的数据记…

浅谈单线程和多线程的异同

前两天有个面试&#xff0c;面试官问了我一个单线程和多线程的问题&#xff0c;情境如下&#xff1a; 面试官&#xff1a;你对单线程和多线程有什么看法&#xff1f; 我&#xff1a; 面试官&#xff1a; 我&#xff1a; 面试官&#xff1a; 我 现在先让我们来了解一下进程…

WS协议—介绍及原理

举例来说&#xff0c;我们想了解今天的天气&#xff0c;只能是客户端向服务器发出请求&#xff0c;服务器返回查询结果。HTTP 协议做不到服务器主动向客户端推送信息。 WebSocket 协议在2008年诞生&#xff0c;2011年成为国际标准。所有浏览器都已经支持了。它的最大特点就是&…

ms17_010(永恒之蓝)漏洞复现详细教程

如题&#xff0c;这是个漏洞复现的详细教程&#xff0c;本教程针对的系统是Windows7操作系统&#xff0c;其他系统请自行测试。 备注&#xff1a;教程会很详细&#xff0c;讲解会很明白&#xff0c;一文可以解决你的常见困难。 测试环境 kalilinux 192.168.1.109 &#xff08;…

Java设计模式之结构型-装饰器模式(UML类图+案例分析)

目录 一、基本概念 二、角色设计 三、代码实现 案例一 案例二 四、总结 一、基本概念 装饰器模式是指不必在改变原有的类和不使用继承的情况下&#xff0c;动态扩展一个对象的功能。 二、角色设计 角色描述抽象构件是一个接口或者抽象类&#xff0c;定义我们最核心的…

科技云报道:边缘计算步入“黄金年代”

科技云报道原创。 当前时点&#xff0c;AI大模型已经站在了从“玩具”向“工具”快速演化的关键迭代期。如何让大模型渗透进入各类垂直场景&#xff0c;如何更低成本的使用大模型&#xff0c;如何让更多场景与用户接触AI&#xff0c;成为了发展的下一个重点。 在AI向实际场景…

大数据应用——总结与反思

1.谈谈你对大数据行业的认识&#xff0c;目前对应的大数据岗位有哪些&#xff1f;每种岗位需要掌握哪些技能水平&#xff1f;目前自己的差距在哪里&#xff1f; &#xff08;1&#xff09;概述 对于大数据行业的认识&#xff0c;我的理解是&#xff0c;大数据是指海量数据&…

8种常见的SQL错误用法

前言&#xff1a;MySQL在2016年仍然保持强劲的数据库流行度增长趋势。越来越多的客户将自己的应用建立在MySQL数据库之上&#xff0c;甚至是从Oracle迁移到MySQL上来。但也存在部分客户在使用MySQL数据库的过程中遇到一些比如响应时间慢&#xff0c;CPU打满等情况。现将《Apsar…

Dumuz同步微信通讯录及常见问题

在Dumuz工具中&#xff0c;【微信通讯录同步】主要功能是从当前登录的微信上下载通讯录相关成员数据。 第1步&#xff1a; 打开应用【微信-消息批量发送】&#xff0c;在工具栏中点击【微信通讯录】如下图所示&#xff1a; 第2步&#xff1a; 进入【微信通讯录】 对话框&#…

【AUTOSAR】AUTOSAR开发工具链(九)----基于BTC的MIL/SIL测试操作说明(1)

一、BTC使用注意事项 1、安装成功后&#xff0c;在Edit->Preference->General->Compiler可以找到编辑器MSVC140 启动BTC&#xff1a;插入电子狗、选择与电子狗相匹配的License、选择相应的工具包 B2B就是MIL V SIL 适用于MBD开发的测试&#xff0c;单独SIL适用于手写…

【Jmeter教程】__将提取的参数并设置成全局变量(常用于提取token)

目录 一、提取参数 1、使用正则表达式提取器提取token 2、使用json提取器提取token 二、将提取参数设置成全局变量 三、常见问题 一、提取参数 1、使用正则表达式提取器提取token 查看登录响应参数找出token。图中token为 "ticketString": "ccf26b17-a96f…

深入理解MySQL主从配置原理

目录 1. MySQL主从复制原理工作原理 2. 主从配置步骤1: 配置主节点2: 备份主节点数据3: 配置从节点4: 启动主从复制 3.常见问题4. 需要考虑的一些因素 MySQL主从复制是一种数据库复制技术&#xff0c;通过将一个MySQL服务器&#xff08;主节点&#xff09;上的数据同步到其他My…

IP地点定位为什么有误差?

随着互联网的不断普及&#xff0c;人们对IP地点定位需求越来越多。然而&#xff0c;即便是在现代技术的支持下IP地点定位仍然存在误差。那么&#xff0c;IP地点定位为什么会出现误差呢&#xff1f; IP&#xff08;Internet Protocol&#xff09;地址是指互联网协议&#xff08;…

SpringCloudSpringcloudAlibaba

SpringCloud 一&#xff1a;微服务架构1.1 ESB1.2 微服务与微服务 二 &#xff1a;编写SpringCloud代码2.1 父模块SpringCloudDemo项目2.2 公共类模块SpringCloud-api项目2.3 消费模块SpringCloud-user-8001项目2.4 RestTemplate 三&#xff1a;注册中心&#xff1a;Eureka3.1 …

机器学习——Kmeans算法

一、实验目的 学习sklearn模块中的KMeans算法 二、实验内容 学习KMeans算法&#xff0c;了解模型创建、使用模型及模型评价等操作 三、实验原理或流程 实验原理&#xff1a; K-means算法是将样本聚类成k个簇(cluster)&#xff0c;具体算法描述如下: 1、随机选取k个聚类质…

春招上岸阿里,好多问题当场尬住!

一个粉丝&#xff0c;23年应届毕业生&#xff0c; 双非本科。最近他校招上岸了&#xff0c;拿到了阿里软件测试岗位的Offer。 他总结了一下面试题&#xff0c;感觉很多内容还是有难度的&#xff0c;尤其是对于应届生来说。下面是他整理的面试题&#xff0c;看看这种校招面试难…

sonarqube本地安装使用

sonarqube本地安装使用 sonarqube本地安装使用配置 官网网址&#xff1a;https://www.sonarqube.org/ 注意&#xff1a;sonarqube版本需要与jdk和数据库版本相对应&#xff0c;以及sonarqube对应电脑的位数 本案例中&#xff1a;JDK8、sonarqube7.6版本 在说明文档中可以…

svg图版绘制

推荐工具&#xff1a;Inkscape 绘制带折线的图版&#xff0c;使用左侧工具栏&#xff08;绘制贝塞尔曲线和直线&#xff09; 选中顶部&#xff08;创建一个直线段构成的折线&#xff09; 直接使用鼠标左键点对点进行绘制&#xff0c;停顿一次为一个坐标&#xff0c;鼠标右击…