手撕Java集合(一)——List集合

简介: Java标准库自带的java.util包提供了集合类:Collection,它是除Map外所有其他集合类的根接口。Java的java.util包主要提供了以下三种类型的集合:List:一种有序列表的集合,例如,按索引排列的Student的List;Set:一种保证没有重复元素的集合,例如,所有无重复名称的Student的Set;Map:一种通过键值(key-value)查找的映射表集合,例如,根据Student的name查找对应Student的Map。...

一、集合

集合主要分为两组(单列集合,双列集合)

  • 单列集合:存放单个元素
  • Collection:两个重要的接口 List Set
ArrayList<String>arrayList=newArrayList<>();
arrayList.add("jack");
arrayList.add("tom");

  • 双列集合:键值对的形式存元素K-V
  • Map
HashMap<String, Object>hashMap=newHashMap<>(64);
hashMap.put("NO1", "北京");
hashMap.put("NO2", "上海");

1.2、Conllection接口和常用方法

1.2.1、常用方法

  • add:添加单个元素
  • remove:删除指定元素
  • contains:查找元素是否存在
  • size:获取元素的个数
  • isEmpty:判断集合是否为空
  • clear:清空集合
  • addAll:添加多个元素
  • containsAll:查找多个元素是否都存在
  • removeAll:删除多个元素
/*** @author java小豪* @date 2022/6/8*/publicclassCollectionMethod {
publicstaticvoidmain(String[] args) {
List<Object>list=newArrayList<>();
// add:添加单个元素list.add("jack");
// list.add(new Integer(10))list.add(10);
list.add(true);
System.out.println("List="+list);
// remove:删除指定元素// 通过下标删除元素list.remove(0);
// 指定删除的元素list.remove(true);
System.out.println("List="+list);
// contains:查找元素是否存在System.out.println(list.contains("jack"));
// size:获取元素的个数System.out.println(list.size());
// isEmpty:判断集合是否为空System.out.println(list.isEmpty());
// clear:清空集合//list.clear();//System.out.println(list);// addAll:添加多个元素ArrayList<Object>arrayList=newArrayList<>();
arrayList.add("红楼梦");
arrayList.add("三国演义");
list.addAll(arrayList);
System.out.println(list);
// containsAll:查找多个元素是否都存在System.out.println(list.containsAll(arrayList));
// removeAll:删除多个元素list.add("聊斋");
list.removeAll(arrayList);
System.out.println(list);
    }
}

1.2.2、Conllection接口遍历元素方式

  • 使用Iterator(迭代器)
/*** @author java小豪* @date 2022/6/8*/publicclassCollectionIterator {
publicstaticvoidmain(String[] args) {
Collection<Object>collection=newArrayList<>();
collection.add(newBook("三国演义", "罗贯中", 50));
collection.add(newBook("小李飞刀", "古龙", 70.0));
collection.add(newBook("红楼梦", "曹雪芹", 60.0));
//        System.out.println("collection = " + collection);// 得到迭代器Iterator<Object>iterator=collection.iterator();
// 使用while循环遍历集合while (iterator.hasNext()) {
Objectobj=iterator.next();
System.out.println("obj = "+obj);
        }
// 重置迭代器iterator=collection.iterator();
while (iterator.hasNext()) {
Objectobj=iterator.next();
System.out.println("obj = "+obj);
        }
    }
}
classBook {
privateStringname;
privateStringauthor;
privatedoubleprice;
publicBook(Stringname, Stringauthor, doubleprice) {
this.name=name;
this.author=author;
this.price=price;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicStringgetAuthor() {
returnauthor;
    }
publicvoidsetAuthor(Stringauthor) {
this.author=author;
    }
publicdoublegetPrice() {
returnprice;
    }
publicvoidsetPrice(doubleprice) {
this.price=price;
    }
@OverridepublicStringtoString() {
return"Book{"+"name='"+name+'\''+", author='"+author+'\''+", price="+price+'}';
    }
}
  • 增强for循环:只能用来遍历集合和数组
  • 基本语法:for(元素类型 元素名 : 集合名或数组名) { 访问元素 }
/*** @author java小豪* @date 2022/6/8*/publicclassCollectionFor {
publicstaticvoidmain(String[] args) {
Collection<Object>collection=newArrayList<>();
collection.add(newBook("三国演义", "罗贯中", 50));
collection.add(newBook("小李飞刀", "古龙", 70.0));
collection.add(newBook("红楼梦", "曹雪芹", 60.0));
// 增强for// 增强for底层仍然是迭代器for (Objectbook : collection) {
System.out.println("book = "+book);
        }
// 增强for用在数组上int[] nums= {1, 6, 7, 9, 10};
for (inti : nums) {
System.out.println("i = "+i);
        }
    }
}

1.3、List接口和常用方法

1.3.1、介绍

1) List集合类中元素有序、且可重复

2) List集合中的每个元素有其对应的顺序索引,即支持索引。

3) List容器中的元素对应一个整数的序号记载在容器中的位置,可以根据序号取出容器中的元素.

/*** @author java小豪* @date 2022/6/9*/publicclassList_ {
publicstaticvoidmain(String[] args) {
// List集合类中元素有序、且可重复List<Object>list=newArrayList<>();
list.add("jack");
list.add("mary");
list.add("tom");
list.add("chen");
System.out.println("list = "+list);
// List集合中的每个元素有其对应的顺序索引,即支持索引。System.out.println(list.get(2));
    }
}

1.3.2、常用方法

  • 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在当前集合中首次出现的位置
  • 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位置的子集合
/*** @author java小豪* @date 2022/6/9*/publicclassListMethod {
publicstaticvoidmain(String[] args) {
List<Object>list=newArrayList<>();
//void add(int index, Object ele):在index位置插入ele元素list.add("张三丰");
list.add("李小龙");
// 在index = 1 的位置插入一个对象list.add(1, "宋江");
System.out.println("list = "+list);
//boolean addAll(int index, Collection eles):从index位置开始将eles中的元素插入List<Object>list2=newArrayList<>();
list2.add("jack");
list2.add("tom");
list.addAll(1, list2);
System.out.println("list = "+list);
//Object get(int index):获取指定index位置的元素System.out.println(list.get(3));
//int indexOf(Object obj):返回obj在当前集合中首次出现的位置System.out.println(list.indexOf("tom"));
//int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置list.add("tom");
System.out.println("list = "+list);
System.out.println(list.lastIndexOf("tom"));
//Object remove(int index):移除指定index位置的元素,并返回此元素list.remove(0);
System.out.println("List = "+list);
//Object set(int index, Object ele):设置指定index位置元素为ele,相当于是替换list.set(1, "玛丽");
System.out.println("List = "+list);
// List subList(int fromIndex, int toIndex):返回从fromIndex到toIndex位置的子集合// 返回子集合范围[fromIndex, toIndex)ListreturnList=list.subList(0, 2);
System.out.println("returnList = "+returnList);
    }
}

1.3.3、练习

  • 添加十个以上的 String "hello"
  • 在2号位插入一个元素
  • 获取第6个元素
  • 删除第7个元素
  • 修改第8个元素
  • 使用迭代器遍历集合
/*** @author java小豪* @date 2022/6/10*/publicclassListExercise {
publicstaticvoidmain(String[] args) {
List<Object>list=newArrayList<>();
// 添加十个以上的 String "hello"for (inti=0; i<12; i++) {
list.add("hello"+i);
        }
System.out.println("list = "+list);
// 在2号位插入一个元素"chen"list.add(1, "chen");
System.out.println("list = "+list);
// 获取第6个元素System.out.println("第6个元素 = "+list.get(5));
// 删除第7个元素list.remove(6);
System.out.println("list = "+list);
// 修改第8个元素list.set(7, "三国演义");
System.out.println("list = "+list);
// 使用迭代器遍历集合Iterator<Object>iterator=list.iterator();
while (iterator.hasNext()) {
Objectobj=iterator.next();
System.out.println("obj = "+obj);
        }
    }
}

1.3.4、List集合的三种遍历方式

  • 迭代器Iterator
  • 增强for
  • 普通for
/*** @author java小豪* @date 2022/6/10*/publicclassListFor {
publicstaticvoidmain(String[] args) {
// List 接口的实现子类 Vector LinkedList// List<Object> list = new ArrayList<>();List<Object>list=newLinkedList<>();
// List<Object> list = new Vector<>();list.add("jack");
list.add("tom");
list.add("天龙八部");
list.add("北京烤鸭");
// 遍历// 1.迭代器Iterator<Object>iterator=list.iterator();
while (iterator.hasNext()) {
Objectobj=iterator.next();
System.out.println("obj = "+obj);
        }
System.out.println("=================");
//2.增强for循环for (Objectx : list) {
System.out.println(x);
        }
System.out.println("================");
//3.普通for循环for (inti=0; i<list.size(); i++) {
System.out.println(list.get(i));
        }
    }
}

1.3.5、List排序练习

Book类

/*** @author java小豪* @date 2022/6/10*/publicclassBook {
privateStringname;
privateStringauthor;
privateintprice;
publicBook(Stringname, Stringauthor, intprice) {
this.name=name;
this.author=author;
this.price=price;
    }
publicStringgetName() {
returnname;
    }
publicvoidsetName(Stringname) {
this.name=name;
    }
publicStringgetAuthor() {
returnauthor;
    }
publicvoidsetAuthor(Stringauthor) {
this.author=author;
    }
publicintgetPrice() {
returnprice;
    }
publicvoidsetPrice(intprice) {
this.price=price;
    }
@OverridepublicStringtoString() {
return"名称:"+name+"\t\t价格:"+price+"\t\t作者:"+author;
    }
}

main:

/*** @author java小豪* @date 2022/6/10*/publicclassListExercise2 {
publicstaticvoidmain(String[] args) {
// List<Object> list = new Vector<>();List<Object>list=newArrayList<>();
// List<Object> list = new LinkedList<>();list.add(newBook("红楼梦", "曹雪芹", 70));
list.add(newBook("西游记", "吴承恩", 10));
list.add(newBook("水浒传", "施耐庵", 90));
list.add(newBook("三国志", "罗贯中", 80));
// 遍历for (Objecto : list) {
System.out.println(o);
        }
// 冒泡排序sort(list);
System.out.println("=========");
// 排序后遍历for (Objecto : list) {
System.out.println(o);
        }
    }
publicstaticvoidsort(List<Object>list) {
for (inti=0; i<list.size() -1; i++) {
for (intj=0; j<list.size() -1-i; j++) {
// 取出对象BookBookbook1= (Book) list.get(j);
Bookbook2= (Book) list.get(j+1);
if (book1.getPrice() >book2.getPrice()) {
list.set(j, book2);
list.set(j+1, book1);
                }
            }
        }
    }
}

1.4、ArrayList底层结构和源码分析

1.4.1、注意事项

  1. ArrayList可以存放空值null
  2. ArrayList是由数组来实现数据存储的
  3. ArrayList是线程不安全的(执行效率高),多线程情况下,不建议使用ArrayList
/*** @author java小豪* @date 2022/6/10*/publicclassArrayListDetail {
publicstaticvoidmain(String[] args) {
ArrayList<String>arrayList=newArrayList<>();
arrayList.add(null);
arrayList.add("jack");
arrayList.add(null);
System.out.println("arrayList = "+arrayList);
    }
}

1.4.2、ArrayList扩容机制

  1. ArrayList中维护一个Object类型的数组elementData transient Object[] elementData;
  2. transient :表示瞬间,短暂的

  1. 当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍。

  1. 如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如需扩容,则直接扩容elementData为1.5倍。

1.4.3、ArrayList源码

一、当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第一次添加,则扩容elementData为10,如需再次扩容,则扩容elementData为1.5倍。

演示代码

/*** @author java小豪* @date 2022/6/10*/publicclassArrayListSource {
publicstaticvoidmain(String[] args) {
// 使用午餐构造器创建ArrayList对象ArrayList<Integer>list=newArrayList<>();
// ArrayList<Integer> list = new ArrayList<>(8);// 使用for给list集合添加 1-10数据for (inti=1; i<=10; i++) {
list.add(i);
        }
// 使用for给list集合添加11-15数据for (inti=11; i<=15; i++) {
list.add(i);
        }
list.add(200);
list.add(100);
    }
}

1.5、Vector底层结构和源码分析

1.5.1、基本介绍

  1. Vector类的定义说明

  1. Vector底层也是一个对象数组,protected Object[] elementData;
  2. Vector是线程同步的,即线程安全,Vector类的操作方法带有synchronized

1.5.2、Vector底层和ArrayList的比较

底层结构

版本

线程安全(同步)效率

扩容倍数

ArrayList

可变数组

JDK1.2

不安全,效率高

如果有参构造1.5倍如果是无参1.第一次102.从第二次开始按照1.5倍

Vector

可变数组

JDK1.0

安全效率高

如果是无参,默认10,满后直接按2倍扩容如果指定大小,则每次直接按2倍扩容

1.5.3、源码解读

一、创建Vector对象时使用无参构造器源码

/*** @author java小豪* @date 2022/6/11*/publicclassVector_ {
publicstaticvoidmain(String[] args) {
// 无参构造器Vector<Integer>vector=newVector<>();
for (inti=0; i<10; i++) {
vector.add(i);
        }
vector.add(100);
System.out.println("vector = "+vector);
// 源码解读// 1. new Vector() 底层/*public Vector() {this(10);}2. vector.add(i)2.1 // 添加数据到Vector集合public synchronized boolean add(E e) {modCount++;ensureCapacityHelper(elementCount + 1);elementData[elementCount++] = e;return true;}2.2  // 确定是否需要扩容 条件: minCapacity - elementData.length > 0private void ensureCapacityHelper(int minCapacity) {// overflow-conscious codeif (minCapacity - elementData.length > 0)grow(minCapacity);}2.3 // 如果需要的数组大小 不够用, 就扩容 扩容算法// int newCapacity = oldCapacity + ((capacityIncrement > 0) ?//                                   capacityIncrement : oldCapacity);private void grow(int minCapacity) {// overflow-conscious codeint oldCapacity = elementData.length;int newCapacity = oldCapacity + ((capacityIncrement > 0) ?capacityIncrement : oldCapacity);if (newCapacity - minCapacity < 0)newCapacity = minCapacity;if (newCapacity - MAX_ARRAY_SIZE > 0)newCapacity = hugeCapacity(minCapacity);elementData = Arrays.copyOf(elementData, newCapacity);}*/    }
}

二、分析图


1.6、LinkedList底层结构

1.6.1、说明

  1. LinkedList实现了双向链表和双端队列特点
  2. 可以添加任意元素(元素可以重复),包括null
  3. 线程不安全,没有实现同步

1.6.2、LinkedList的底层操作机制

  1. LinkedList底层维护了一个双向链表
  2. LinkedList中维护了两个属性first和last分别指向首节点和尾节点
  3. 每个节点(Node对象),里面有维护了prev、next、item三个属性,其中通过prev指向前一个,通过next指向后一个节点。最终实现双向链表
  4. LinkedList的元素添加和删除,不是通过数组完成的,相对来说效率较高
  5. 模拟简单的双向链表
/*** @author java小豪* @date 2022/6/11*/publicclassLinkedList01 {
publicstaticvoidmain(String[] args) {
// 模拟简单的双向链表Nodejack=newNode("jack");
Nodetom=newNode("tom");
Nodechen=newNode("chen");
// 连接三个节点,形成双向链表// jack -> tom -> chenjack.next=tom;
tom.next=chen;
// chen -> tom -> jackchen.prev=tom;
tom.prev=jack;
// 让first引用指向jack,就是双向链表的头节点Nodefirst=jack;
// 让last引用指向chen,就是双向链表的尾节点Nodelast=chen;
// 遍历 从头到尾遍历while (true) {
if (first==null) {
break;
            }
// 输出信息System.out.println(first);
first=first.next;
        }
// 遍历 从尾到头遍历System.out.println("===从尾到头遍历===");
while (true) {
if (last==null) {
break;
            }
// 输出信息System.out.println(last);
last=last.prev;
        }
// 添加元素// 1.创建一个Node结点, name 是张飞Nodezf=newNode("张飞");
zf.next=chen;
zf.prev=tom;
chen.prev=zf;
tom.next=zf;
// 让first 再次指向jackfirst=jack;
System.out.println("===从头到尾遍历===");
while (true) {
if (first==null) {
break;
            }
// 输出信息System.out.println(first);
first=first.next;
        }
    }
}
/*** 定义一个Node类*/classNode {
/**存放数据*/publicObjectitem;
/**指向下一个节点*/publicNodenext;
/**指向前一个节点*/publicNodeprev;
publicNode(Objectname) {
this.item=name;
    }
publicStringtoString() {
return"Node name = "+item;
    }
}

1.6.3、LinkedList底层源码

1.6.3.1、添加元素

  • linkedList.add(1);
// 1、LinkedList<Integer> linkedList = new LinkedList<>();publicLinkedList() {}
// 2、这是LinkedList 的属性first = null last = null// 3、执行add方法publicbooleanadd(Ee) {
linkLast(e);
returntrue;
   }
// 4、将新的结点,加入到双向链表的最后voidlinkLast(Ee) {
finalNode<E>l=last;
finalNode<E>newNode=newNode<>(l, e, null);
last=newNode;
if (l==null)
first=newNode;
elsel.next=newNode;
size++;
modCount++;
    }
  • 流程图

  • 执行 add 方法

  • 执行 linkList

1.6.3.2、删除元素

  • linkedList.remove();
// 1、linkedList.remove();// 默认删除第一个元素// 2、执行 removeFirst() 方法publicEremove() {
returnremoveFirst();
    }
// 3、执行publicEremoveFirst() {
finalNode<E>f=first;
if (f==null)
thrownewNoSuchElementException();
returnunlinkFirst(f);
    }
// 4、执行  unlinkFirst, 将 f 指向的双向链表的第一个结点拿掉privateEunlinkFirst(Node<E>f) {
// assert f == first && f != null;finalEelement=f.item;
finalNode<E>next=f.next;
f.item=null;
f.next=null; // help GCfirst=next;
if (next==null)
last=null;
elsenext.prev=null;
size--;
modCount++;
returnelement;
    }
  • 流程图

1.6.3.3、LinkedList的其他方法

  • get()
  • remove(int index)
  • set(int index)

等等的源码都可以依照以上方法进行追溯。


目录
相关文章
|
11小时前
|
安全 Java 开发者
【JAVA】哪些集合类是线程安全的
【JAVA】哪些集合类是线程安全的
|
11小时前
|
存储 安全 Java
Java一分钟之-集合框架进阶:Set接口与HashSet
【5月更文挑战第10天】本文介绍了Java集合框架中的`Set`接口和`HashSet`类。`Set`接口继承自`Collection`,特征是不允许重复元素,顺序不确定。`HashSet`是`Set`的实现,基于哈希表,提供快速添加、删除和查找操作,但无序且非线程安全。文章讨论了`HashSet`的特性、常见问题(如元素比较规则、非唯一性和线程安全性)以及如何避免这些问题,并提供了代码示例展示基本操作和自定义对象的使用。理解这些概念和注意事项能提升代码效率和可维护性。
11 0
|
11小时前
|
存储 安全 算法
Java一分钟之-Java集合框架入门:List接口与ArrayList
【5月更文挑战第10天】本文介绍了Java集合框架中的`List`接口和`ArrayList`实现类。`List`是有序集合,支持元素重复并能按索引访问。核心方法包括添加、删除、获取和设置元素。`ArrayList`基于动态数组,提供高效随机访问和自动扩容,但非线程安全。文章讨论了三个常见问题:索引越界、遍历时修改集合和并发修改,并给出避免策略。通过示例代码展示了基本操作和安全遍历删除。理解并正确使用`List`和`ArrayList`能提升程序效率和稳定性。
7 0
|
11小时前
|
存储 安全 Java
【JAVA基础篇教学】第八篇:Java中List详解说明
【JAVA基础篇教学】第八篇:Java中List详解说明
|
11小时前
|
存储 安全 Java
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
Java容器类List、ArrayList、Vector及map、HashTable、HashMap
|
11小时前
|
存储 安全 算法
掌握Java并发编程:Lock、Condition与并发集合
掌握Java并发编程:Lock、Condition与并发集合
12 0
|
11小时前
|
存储 安全 Java
深入理解Java集合框架
深入理解Java集合框架
11 0
|
11小时前
|
存储 安全 Java
Java集合的分类有哪些?
Java中的集合就像一个容器,专门用来存储Java对象,这些对象可以是任意的数据类型,并且长度可变。这些集合类都位于java.util包中,在使用时一定要注意导包的问题,否则会出现异常。
37 10
|
11小时前
|
安全 Java
循环的时候去删除集合中的元素 java.util.ConcurrentModificationException
循环的时候去删除集合中的元素 java.util.ConcurrentModificationException
|
11小时前
|
Java API
【亮剑】三种有效的方法来删除List中的重复元素Java的List
【4月更文挑战第30天】本文介绍了三种Java中删除List重复元素的方法:1) 使用HashSet,借助其不允许重复值的特性;2) 利用Java 8 Stream API的distinct()方法;3) 对自定义对象重写equals()和hashCode()。每种方法都附带了代码示例,帮助理解和应用。