手撕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)

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


目录
相关文章
|
1月前
|
算法 Java 数据处理
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。
从HashSet到TreeSet,Java集合框架中的Set接口及其实现类以其“不重复性”要求,彻底改变了处理唯一性数据的方式。HashSet基于哈希表实现,提供高效的元素操作;TreeSet则通过红黑树实现元素的自然排序,适合需要有序访问的场景。本文通过示例代码详细介绍了两者的特性和应用场景。
41 6
|
1月前
|
存储 Java
深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。
【10月更文挑战第16天】本文深入探讨了Java集合框架中的HashSet和TreeSet,解析了两者在元素存储上的无序与有序特性。HashSet基于哈希表实现,添加元素时根据哈希值分布,遍历时顺序不可预测;而TreeSet利用红黑树结构,按自然顺序或自定义顺序存储元素,确保遍历时有序输出。文章还提供了示例代码,帮助读者更好地理解这两种集合类型的使用场景和内部机制。
39 3
|
1月前
|
存储 Java 数据处理
Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位
【10月更文挑战第16天】Java Set接口凭借其独特的“不重复”特性,在集合框架中占据重要地位。本文通过快速去重和高效查找两个案例,展示了Set如何简化数据处理流程,提升代码效率。使用HashSet可轻松实现数据去重,而contains方法则提供了快速查找的功能,彰显了Set在处理大量数据时的优势。
33 2
|
15天前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
25 2
|
14天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
19天前
|
存储 Java
判断一个元素是否在 Java 中的 Set 集合中
【10月更文挑战第30天】使用`contains()`方法可以方便快捷地判断一个元素是否在Java中的`Set`集合中,但对于自定义对象,需要注意重写`equals()`方法以确保正确的判断结果,同时根据具体的性能需求选择合适的`Set`实现类。
|
19天前
|
存储 Java 开发者
在 Java 中,如何遍历一个 Set 集合?
【10月更文挑战第30天】开发者可以根据具体的需求和代码风格选择合适的遍历方式。增强for循环简洁直观,适用于大多数简单的遍历场景;迭代器则更加灵活,可在遍历过程中进行更多复杂的操作;而Lambda表达式和`forEach`方法则提供了一种更简洁的函数式编程风格的遍历方式。
|
19天前
|
Java 开发者
|
1月前
|
安全 Java 程序员
深入Java集合框架:解密List的Fail-Fast与Fail-Safe机制
本文介绍了 Java 中 List 的遍历和删除操作,重点讨论了快速失败(fail-fast)和安全失败(fail-safe)机制。通过普通 for 循环、迭代器和 foreach 循环的对比,详细解释了各种方法的优缺点及适用场景,特别是在多线程环境下的表现。最后推荐了适合高并发场景的 fail-safe 容器,如 CopyOnWriteArrayList 和 ConcurrentHashMap。
58 5
|
30天前
|
Java 程序员 编译器
Java|如何正确地在遍历 List 时删除元素
从源码分析如何正确地在遍历 List 时删除元素。为什么有的写法会导致异常,而另一些不会。
25 3

热门文章

最新文章

下一篇
无影云桌面