集合框架 Collection - List(Arrays.asList)

Arrays.asList()

java 复制代码
关于:Collection coll1 = Arrays.asList(123, 4567);

1、
Arrays.asList()方法属于"Arrays类",返回的是一个"java.util.Arrays.ArrayList"(这是一个 AbstractList的私有静态内部类,不是"java.util.ArrayList")
所以,使用 Arrays.asList(123, 4567) 时,需要导入 "import java.util.Arrays"

Arrays.asList(),返回的是AbstractList的一个实例,
  这里发生了向上转型,即一个更具体的类(AbstractList)的实例被赋值给了一个更一般的接口(Collection)类型的变量。

2、
Arrays.asList(123, 4567),详解如下:
  
  将一组元素,转换为,一个固定大小的列表(List)的静态方法,

  需要注意的是,返回的列表(List)大小是固定的,不能添加或删除元素,
  
  如果,你尝试添加、删除,将会抛出"UnsupportedOperationException"。

  另,
    如果你需要一个可以修改的列表,
    应该使用 new ArrayList<>(Arrays.asList(123, 4567)) 来创建一个新的 ArrayList 实例,这样就可以修改它的内容了。

3、代码示例
  import java.util.ArrayList;
  import java.util.Arrays;
  import java.util.Collection;
  import java.util.List;  
  public static void main(String[] args) {
    // 创建一个不可修改的固定大小列表
    List<Integer> fixedList = Arrays.asList(123, 4567);
    // 使用固定大小列表创建一个可修改的ArrayList
    List<Integer> modifiableList = new ArrayList<>(fixedList);
    System.out.println("Fixed List= " + fixedList); // Fixed List= [123, 4567]
    System.out.println("Modifiable List= " + modifiableList); // Modifiable List= [123, 4567]

    // 向可修改的ArrayList中添加一个新元素
    modifiableList.add(8910);
    System.out.println("Modifiable= " + modifiableList); // Modifiable= [123, 4567, 8910]

    // 尝试向固定大小的列表添加元素,这将抛出 UnsupportedOperationException 的异常
    fixedList.add(9876); // Exception in thread "main" java.lang.UnsupportedOperationException
  }

17、集合框架

java 复制代码
1、"集合的概述":集合、数组都是对多个数据进行存储操作的结构,简称Java容器。

java集合类,可以用与存储数量不等的多个对象,还可用于保存具有映射关系的关联数组。


2、集合可分为"Collection" 和 "Map"两种体系,如下,

  a、Collection接口:单例数据,用来存随一个一个的对象,定义了存取一组对象的方法集合
    
      List:元素有序,可重复的集合

      Set:元素无序,不可重复的集合

  b、Map接口,双列数据,用来存随一对一对的,保存具有映射关系"key-value对"的集合

Collection接口

java 复制代码
Collection接口:集合框架的顶级接口,所有接口都是从"Collection接口"继承过来的,
                是"Set 和 List的父接口",但"不是Map的父接口"。
常用方法-1
java 复制代码
  1、add(Object e):将元素e添加到集合中

  2、size():获取添加元素的个数

  3、addAll(Collection coll):将coll集合中的元素添加到当前的集合中

  4、clear():清空集合元素

  5、isEmpty():判断当前集合是否为空

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;

public static void main(String[] args) {
  Collection coll = new ArrayList();

  // add(Object e):将元素e添加到集合中
  coll.add("AA");
  coll.add("BB");
  coll.add(123); // 自动装箱
  coll.add(new Date());
  System.out.println("coll= " + coll); // [AA, BB, 123, Wed Feb 28 15:15:51 CST 2024]

  // size():获取添加元素的个数
  System.out.println(coll.size()); // 4

  Collection coll2 = new ArrayList();
  coll2.add(456);
  coll2.add("CC");
  System.out.println("coll2= " + coll2); // coll2= [456, CC]

  // addAll(Collection coll):将coll集合中的元素添加到当前的集合中
  coll.addAll(coll2);
  System.out.println("coll= " + coll); // coll= [AA, BB, 123, Wed Feb 28 15:17:44 CST 2024, 456, CC]
  System.out.println(coll.isEmpty()); // false

  // clear():清空集合元素
  coll.clear();
  System.out.println("coll= " + coll); // []

  // isEmpty():判断当前集合是否为空
  System.out.println(coll.isEmpty()); // true
}
常用方法-2
java 复制代码
1、contains(Object obj):判断当前集合中是否包含obj

2、containsAll(Collection coll):判断形参coll中的所有元素,是否都存在于当前集合中

3、remove(Object obj):从当前集合中移除obj元素

4、removeAll(Collection coll):差集,从当前集合中移除coll中所有的元素

5、retainAll(Collection coll):交集,获取当前集合和coll集合的交集,并返回给当前集合

6、equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同

7、hashCode():返回当前对象的哈希值

8、集合 ---> 数组:toArray()

"注意":向Collection接口的实现类的对象中,添加数据obj时,要求obj所在类要重写equals()
java 复制代码
public static void main(String[] args) {
  Collection coll = new ArrayList();
  coll.add(123);
  coll.add(456);
  coll.add(789);
  coll.add(new String("Tom"));
  System.out.println(coll); // [123, 456, 789, Tom]

  // ---------- contains(Object obj):判断当前集合中是否包含obj ----------
  // 我们在判断时,会调用obj对象所在类的equals()
  boolean contains1 = coll.contains(123);
  boolean contains2 = coll.contains(1234);
  System.out.println(contains1); // true
  System.out.println(contains2); // false

  // ---------- containsAll(Collection coll):判断形参coll中的所有元素是否都存在于当前集合中 ----------
  Collection coll2 = Arrays.asList(123,456);
  System.out.println(coll.containsAll(coll2)); // true
  Collection coll3 = Arrays.asList(123,4567);
  System.out.println(coll.containsAll(coll3)); // false

  // ---------- remove(Object obj):从当前集合中移除obj元素 ----------
  coll.remove(123);
  System.out.println(coll); // [456, 789, Tom]

  // ---------- removeAll(Collection coll):差集,从当前集合中移除coll中所有的元素 ----------
  Collection coll1 = Arrays.asList(123,456);
  coll.removeAll(coll1);
  System.out.println(coll); // [789, Tom]
}
java 复制代码
public static void main(String[] args) {
  Collection coll = new ArrayList();
  coll.add(123);
  coll.add(456);
  coll.add(789);
  coll.add(new String("Tom"));
  System.out.println(coll); // [123, 456, 789, Tom]

  // ---------- retainAll(Collection coll):交集,获取当前集合和coll集合的交集,并返回给当前集合 ----------
  Collection coll2 = Arrays.asList(123,456,789);
  coll.retainAll(coll2);
  System.out.println(coll); // [123, 456, 789]

  // ---------- equals(Object obj):要想返回true,需要当前集合和形参集合的元素都相同 ----------
  System.out.println(coll.equals(coll2)); // true

  coll.add(000);
  System.out.println(coll.equals(coll2)); // false
}
java 复制代码
public static void main(String[] args) {
  Collection coll = new ArrayList();
  coll.add(123);
  coll.add(456);
  coll.add(789);
  coll.add(new String("Tom"));
  System.out.println(coll); // [123, 456, 789, Tom]

  // ---------- hashCode():返回当前对象的哈希值 ----------
  System.out.println(coll.hashCode()); // 5134763

  // ---------- 集合 ---> 数组:toArray() ----------
  Object[] arr = coll.toArray();
  System.out.println(Arrays.toString(arr)); // [123, 456, 789, Tom]
  for (int i = 0; i < arr.length; i++) {
    if(i != arr.length-1){
        System.out.print(arr[i] + "-");
    } else {
        System.out.print(arr[i]);
    }
  }
  // 这个for循环,最终打印的是:123-456-789-Tom

  // ---------- 拓展:数组 -> 集合:调用Arrays类的静态方法asList() ----------
  List<String> list = Arrays.asList(new String[]{"AA", "BB", "CC"});
  System.out.println(list); // [AA, BB, CC]

  List arr1 = Arrays.asList(new int[]{123, 456});
  System.out.println(arr1.size()); // 1

  List arr2 = Arrays.asList(new Integer[]{123, 456});
  System.out.println(arr2.size()); // 2
}
迭代器接口
js 复制代码
GOP给迭代器模式定义为:

  提供一种方法,访问容器对象中各个元素,而又不需暴露该对象内部细节,迭代器模式,就是为容器而生

  用于遍历Collection集合中的元素,"Collection接口继承了java.long.Iterable接口",该接口有一个 iterator()方法,
  
  那么,所有实现了Collection接口的集合类,都有一个 iterator()方法。
java 复制代码
/* 
  集合元素的遍历操作,使用迭代器Iterator接口,

  1、集合对象(Collection对象),每次调用 iterator() 方法,都得到一个全新的迭代器对象

  2、hasNext() 和 next() 方法
      hasNext()方法,用于检查集合中是否还有下一个元素。
        如果,迭代器指向的集合中还有元素,则此方法返回 true;否则返回 false。
      
      next()方法,用于返回迭代器指向的下一个元素,并将迭代器向前移动一位。
        如果,迭代器已经指向集合的末尾(即没有下一个元素),则调用此方法会抛出 NoSuchElementException。
  
  3、默认游标,都在集合的第一个元素之前:
      当你首次获取一个迭代器时,它的"游标"或"位置",位于集合的第一个元素之前,
      调用 next() 方法,会使游标移动到第一个元素,
      随后的 next() 调用,会继续移动游标到下一个元素。

  4、内部定义了 remove() 方法
      Iterator接口中的remove()方法,允许,在迭代过程中,安全地删除集合中的当前元素(即上一个被 next() 方法返回的元素),
      这是 Iterator 特有的,不同于集合直接调用 remove() 方法,因为集合的 remove() 方法需要知道要删除的确切元素。

      在迭代过程中使用Iterator.remove(),可以避免,在遍历过程中由于集合结构的变化(如直接调用remove()方法)而引发的ConcurrentModificationException。
*/
java 复制代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public static void main(String[] args) {
   coll = new ArrayList();
  coll.add(123);
  coll.add(456);
  coll.add(789);
  coll.add(new String("Tom"));

  Iterator iterator = coll.iterator();
  // hasNext():判断是否还有下一个元素
  while (iterator.hasNext()) {
    // next():1、指针下移,2、将下移以后集合位置上的元素返回
    System.out.println(iterator.next());
  }

  // 错误方式一:
  Iterator iterator = coll.iterator();
  while((iterator.next()) != null){
    System.out.println(iterator.next());
  }

  // 错误方式二:
  /*
    每次调用coll.iterator().hasNext() 和 coll.iterator().next()都会创建一个新的迭代器,
    而不是使用已经存在的iterator变量,
    因为,每次调用coll.iterator()都会返回一个新的迭代器,并且迭代器是独立的,它们不会互相影响,因此循环永远不会结束。
  */
  // 这个 while 会死循环
  while (coll.iterator().hasNext()){
      System.out.println(coll.iterator().next());
  }
}
java 复制代码
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public static void main(String[] args) {
  Collection coll = new ArrayList();
  coll.add(123);
  coll.add(456);
  coll.add("Tom");
  coll.add(789);

  Iterator iterator = coll.iterator();
  // hasNext():判断是否还有下一个元素
  while (iterator.hasNext()) {
      Object obj = iterator.next();
      if ("Tom".equals(obj)) {
          // 使用迭代器的remove()方法,删除当前元素
          iterator.remove();
          System.out.println("Removed: " + obj);
      } else {
          System.out.println("Kept: " + obj);
      }
  }
}
/* 
打印的是,
  Kept: 123
  Kept: 456
  Removed: Tom
  Kept: 789
*/

Collection - List

java 复制代码
 List接口的实现类有:ArrayList LinkedList Vector
java 复制代码
1、List 接口的常用方法:

  void add(int index, Object ele):在index位置插入ele元素

  boolean addAll(int index, Collection eles):从index位置开始,将eles中的所有元素添加进来

  Object get(int index):获取指定index位置的元素

  int indexOf(Object obj):返回obj在集合中首次出现的位置,如果不存在 返回-1

  int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置

  Object remove(int index):移除指定index位置的元素,并返回此元素

  Object set(int index, Object ele):设置指定index位置的元素为ele

  List subList(int fromIndex, int toIndex):返回,从fromIndex到toIndex位置的子集合

2、总结:常用方法
    
    增:add(Object obj)

    删:remove(int index) / remove(Object obj)

    改:set(int index, Object ele)

    查:get(int index)

    插:add(int index, Object ele)

    长度:size()

    遍历:Iterator迭代器方式
            增强for循环
            普通的循环
java 复制代码
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public static void main(String[] args) {
  ArrayList list = new ArrayList();
  list.add(123);
  list.add(456);
  list.add("AA");
  list.add(456);
  System.out.println("list=" + list); // list=[123, 456, AA, 456]

  // void add(int index, Object ele):在index位置插入ele元素 -----------------------------------
  list.add(1, "BB");
  System.out.println(list); // [123, BB, 456, AA, 456]

  // boolean addAll(int index, Collection eles):从index位置开始,将eles中的所有元素添加进来 -----------------------------------
  List<Integer> list1 = Arrays.asList(1, 2, 3);
  list.addAll(list1);
  System.out.println(list); // [123, BB, 456, AA, 456, 1, 2, 3]

  // Object get(int index):获取指定index位置的元素 -----------------------------------
  System.out.println(list.get(1)); // BB

  // int indexOf(Object obj):返回obj在集合中首次出现的位置 -----------------------------------
  System.out.println(list.indexOf(456)); // 2
  System.out.println(list.indexOf(456789)); // -1

  // int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置 -----------------------------------
  System.out.println(list.lastIndexOf(456)); // 4

  // Object remove(int index):移除指定index位置的元素,并返回此元素 -----------------------------------
  Object o = list.remove(0);
  System.out.println(o); // 123
  System.out.println(list); // [BB, 456, AA, 456, 1, 2, 3]

  // Object set(int index, Object ele):设置指定index位置的元素为ele -----------------------------------
  list.set(1, "CC");
  System.out.println(list); // [BB, CC, AA, 456, 1, 2, 3]

  // List subList(int fromIndex, int toIndex):返回,从[fromIndex, toIndex)位置的子集合 -----------------------------------
  List list2 = list.subList(0, 3);
  System.out.println(list2); // [BB, CC, AA]

  // 遍历List -----------------------------------
  // 遍历方式 - 1、迭代器
  Iterator iterator = list.iterator();
  while(iterator.hasNext()){
    System.out.println(iterator.next());
  }

  // 遍历方式 - 2、增强for
  for (Object obj: list){
    System.out.println(obj);
  }

  // 遍历方式 - 2、普通for
  for (int i = 0;i < list.size();i++){
    System.out.println(list.get(i));
  }
}
ArrayList
java 复制代码
1、
  内部存储用的数据结构,是用**数组(动态调整大小)**实现,默认初始容量为10。每次扩容大小是增加50%(在java8版本以及之后的版本,java6使用的是1.5倍)。

2、
  优点:
    使用数组实现,因此,内部元素可以通过索引实现快速随机访问。

  缺点:
    a. 从ArrayList中间位置插入和删除元素,都需要循环移动其他元素元素的位置。

    b. 数组空间不够需要扩容时,会开辟一个新的数组,把旧的数组元素拷贝过去,比较耗性能。

    c. 线程不安全。

3、扩容

  java8及之后,扩容计算方法:
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    这意味着,在原来数组大小的基础上,扩大50%作为新数组容量的大小。

  java6的计算方法:
    int newCapacity = (oldCapacity * 3)/2 + 1;
    这意味着,在原来数组大小的基础上,扩大1.5倍作为新数组容量的大小。

4、
  "总之,ArrayList基于数组实现 查改快,增删慢,线程不安全"

  "在需要做一次性插入 和 多次查询业务时,可以使用此集合,但是,ArrayList不保证线程安全,只能在单线程时候做使用"
源码解析
java 复制代码
"源码":
  class ArrayList<E> extends AbstractList<E> 
    implements List<E>, RandomAccess, Cloneable, java.io.Serializable
js 复制代码
  "AbstractList<E>":是List接口第一抽象类

  "RandomAccess":RandomAccess接口是一个标志接口(Marker)它支持快速随机访问

  "Cloneable":支持克隆

  "java.io.Serializable":RandomAccess接口也是是一个标志接口(Marker) 它支持序列化和反序列化
java 复制代码
"属性":
  private static final long serialVersionUID = 8683452581122892189L; // 序列化编号

  private static final int DEFAULT_CAPACITY = 10; // 使用无参构造器时,第一次扩容的数组默认大小10

  private static final Object[] EMPTY_ELEMENTDATA = {}; // 用于空实例的共享空数组实例

  // 用于,默认大小的空实例的共享空数组实例。
  // 我们将其与 EMPTY_ELEMENTDATA 区分开来,以了解添加第一个元素时扩容多少。
  // MARK:无参构造函数,使用该数组初始化,与 EMPTY_ELEMENTDATA 的区别主要是区分作用,用来减少空数组的存在,优化内存使用 1.8后的优化
  private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

  transient Object[] elementData; // 底层的数据结构Object类型的数组

  private int size; // 记录数组中元素的个数


"无参构造器 - 构造一个初始容量为10的一个数组":
  public ArrayList() {
    // private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    /*
      这里其实是赋值了一个共享的空数组,
      数组在第一次添加元素时,会判断 elementData 是否等于 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,
      假如等于,则会初始化容量为 DEFAULT_CAPACITY 也就是10
    */
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
  }


"带参构造器 - 1":
int initialCapacity为ArrayList 底层数组初始的长度,构造一个指定长度的数组
public ArrayList(int initialCapacity) {
  // 参数合法性检验
  if (initialCapacity > 0) {
    // 创建一个长度为 initialCapacity 的数
    this.elementData = new Object[initialCapacity];

  } else if (initialCapacity == 0) {
    // 将原有的空数组EMPTY_ELEMENTDATA赋给底层数组elementData
    this.elementData = EMPTY_ELEMENTDATA;

  } else {
    // 参数不合法抛出IllegalArgumentException异常
    throw new IllegalArgumentException("Illegal Capacity: "+initialCapacity);
  }
}


"带参构造器 - 2"
"Collection<? extends E> c":c是泛型E的子类,构造一个包含指定集合的元素的列表,按照它们由集合的迭代器返回的顺序。
public ArrayList(Collection<? extends E> c) {
  Object[] a = c.toArray(); // 将c集合中的数据拷贝到a数组中
  if ((size = a.length) != 0) { // 判空并给size赋值
    if (c.getClass() == ArrayList.class) { // 如果c是ArrayList类的对象之间将a数组地址赋值给elementData数组
      elementData = a; 
    } else {
      // 进行拷贝
      elementData = Arrays.copyOf(a, size, Object[].class);
    }
  } else {
    // 如果为c集合为空,则将原有的空数组EMPTY_ELEMENTDATA赋给底层数组elementData
    elementData = EMPTY_ELEMENTDATA;
  }
}


"扩容机制"
private Object[] grow() { // 数组容量不足时,直接调用的扩容方法,返回一个Object数组
  return grow(size + 1);
}

private Object[] grow(int minCapacity) { // 数组拷贝的实现,newCapacity(minCapacity) 为实际的扩容大小
  return elementData = Arrays.copyOf(elementData, newCapacity(minCapacity));
}

private int newCapacity(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;//原来数组的长度
  int newCapacity = oldCapacity + (oldCapacity >> 1); //新数组的长度,在原来数组的长度上加原来数组的二分之一
  if (newCapacity - minCapacity <= 0) { //如果新数组的长度小于等于size+1 比如原来数组长度为0或者1,那么新数组长度也为0或者1无法体现扩容,则需要重新处理:处理如下
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
      // 判断,现在的数组,是否为无参构造器创建的数组,如果是,就返回DEFAULT_CAPACITY,即无参构造器第一次扩容为10
      return Math.max(DEFAULT_CAPACITY, minCapacity);
      
    if (minCapacity < 0) // 参数无效抛出异常
      throw new OutOfMemoryError();

    return minCapacity; //扩容+1;
  }

  // MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
  return (newCapacity - MAX_ARRAY_SIZE <= 0) // 如果,新数组长度,大于最大扩容边界需要做特殊处理
      ? newCapacity
      : hugeCapacity(minCapacity); // 特殊处理
}

private static int hugeCapacity(int minCapacity) {
  // 参数合法性检验
  if (minCapacity < 0) // overflow
    throw new OutOfMemoryError();
    
  return (minCapacity > MAX_ARRAY_SIZE) // 如果,size+1大于最大扩容边界,则扩容到 Integer.MAX_VALUE(2 的 31 次方 - 1),否则扩容到最大扩容边界
    ? Integer.MAX_VALUE
    : MAX_ARRAY_SIZE;
}
Vector
java 复制代码
1、
  基于 数组(动态调整大小) 数据结构实现,初始容量是10。

2、
  优点:线程安全。

  缺点:效率低,增加元素、删除元素、查找元素都很慢。
java 复制代码
1、
  内部存储用的数据结构,是用双向链表实现。

2、
  优点:
    使用链表实现,适合动态的插入和删除。

  缺点:
    a. 随机访问元素的速度相对较慢。
    
    b. 基于链表数据结构的实现,占用的内存空间比较大(除了保存数据本身,还要保存指针信息)。

"总之",
  LinkedList,底层通过双向链表的形式实现,增删快,遍历和查询慢,线程不安全。

  LinkedList,底层使用双向链表的形式存储数据,不用向ArrayList存在地址浪费,并且在增删时效率高,我们可以在需要经常增删时使用此集合来提高我们的效率。
java 复制代码
"源码":
  public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable
  
  与 ArrayList 不同的是,
  LinkedList 继承的是 AbstractSequentialList,
  但,AbstractSequentialList 也是继承自 AbstractList,
  其他和 ArrayList 相同
java 复制代码
"属性"
  transient int size = 0; // 记录数据多少
  transient Node<E> first; // 双向链表的头结点
  transient Node<E> last; //	双向链表的尾结点


"构造方法"
public LinkedList() {} // LinkedList的构造方法,是一个空方法,此时指针变量first和last的初始值都为null。

public LinkedList(Collection<? extends E> c) { // 拷贝的构造器
  this();
  addAll(c); // 将所有c中的数据拷贝过来
}


"链表节点静态类,储存的数据的实体Node"
private static class Node<E> {
  E item; // 需要储存的数据
  Node<E> next; // 后继,连接下一个数据,如果它是最后一个,则为null
  Node<E> prev; // 前驱,连接上一个数据,如果它是第一个,则为null

  Node(Node<E> prev, E element, Node<E> next) { // Node的构造器
    this.item = element;
    this.next = next;
    this.prev = prev;
  }
  // 明显看出,这是一个双向链表节点,item是用来存放节点值,next是尾指针指向下一个节点,prev是头指针指向前一个节点。
}
相关推荐
F-2H41 分钟前
C语言:指针4(常量指针和指针常量及动态内存分配)
java·linux·c语言·开发语言·前端·c++
苹果酱056744 分钟前
「Mysql优化大师一」mysql服务性能剖析工具
java·vue.js·spring boot·mysql·课程设计
_oP_i2 小时前
Pinpoint 是一个开源的分布式追踪系统
java·分布式·开源
mmsx2 小时前
android sqlite 数据库简单封装示例(java)
android·java·数据库
武子康2 小时前
大数据-258 离线数仓 - Griffin架构 配置安装 Livy 架构设计 解压配置 Hadoop Hive
java·大数据·数据仓库·hive·hadoop·架构
豪宇刘3 小时前
MyBatis的面试题以及详细解答二
java·servlet·tomcat
秋恬意3 小时前
Mybatis能执行一对一、一对多的关联查询吗?都有哪些实现方式,以及它们之间的区别
java·数据库·mybatis
FF在路上4 小时前
Knife4j调试实体类传参扁平化模式修改:default-flat-param-object: true
java·开发语言
真的很上进4 小时前
如何借助 Babel+TS+ESLint 构建现代 JS 工程环境?
java·前端·javascript·css·react.js·vue·html
众拾达人5 小时前
Android自动化测试实战 Java篇 主流工具 框架 脚本
android·java·开发语言