一、集合的理解:将多个数据放在一起

简介:

1)、可以动态保存任意多个对象,使用比较方便!
2)、提供了一系列方便的操作对象的方法: add、remove、set、 get等

1、集合中的实现和继承图

(单列集合list,set:单一的数据、元素)

(双列集合:键值对)

 二、Collection接口和常用方法

1、Collection接口实现类的特点;

1) collection实现子类可以存放多个元素,每个元素可以是Object

2)有些Collection的实现类,可以存放重复的元素,有些不可以
3)有些Collection的实现类,有些是有序的(List),有些不是有序(Set)

4)Collection接口没有直接的实现子类,是通过它的子接口Set 和 List来实现的

2、Collection常用方法

3、Collection接口遍历元素方式

1);使用迭代器(Iterator)

(1) lterator对象称为迭代器,主要用于遍历Collection集合中的元素。

(2))所有实现了Collection接口的集合类都有一个iterator()方法,用以返回
一个实现了Iterator接口的对象,即可以返回一个迭代器。
(3) lterator的结构.[图:]
(4) lterator仅用于遍历集合,Iterator本身并不存放对象。

(5)快速生成迭代器功能:itit

迭代器的执行原理:

流程:

提示:在调用iterator.next()方法之前必须要调用iterator.hasNext()进行检测。若不调用,且下一条记录无效,直接调用it.next()会抛出(NoSuchElementException)异常。

2)、使用增强for循环遍历*(foreach):快速生成增强for功能:输入字母:I 再 enter

(1):增强for在底层仍然是迭代器iterator,简化版的iterator

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator; public class Test {
public static void main(String[] args) {
Collection<People> people = new ArrayList<>();
people.add(new People("z",12));
people.add(new People("3",22));
people.add(new People("4",42));
//迭代器遍历
Iterator iterator = people.iterator();
while (iterator.hasNext()) {
Object next = iterator.next();
System.out.println(next);
}
//增强for循环遍历
for (People p:people
) {
System.out.println("for " + p);
} }
}

三、Collection下的List:list是Collection的子类

1、List接口和常用方法

List接口基本介绍
List 接口是Collection 接口的子接口 
1)List集合类中元素有序(即添加顺序和取出顺序一致)、且可重复

2)List集合中的每个元素都有其对应的顺序索引,即支持索引。
3)List容器中的元素都对应一个整数型的序号记载其在容器中的位置,可以根
据序号存取容器中的元素。

2、List接口的常用方法:

List集合里添加了一些根据索引来操作集合元素的方法

1)void add(int index, Object ele):在index位置插入ele元素
2)boolean addAll(int index, Collection eles):从index位置开始将
eles中的所有元素添加进来
3) Object get(int index):获取指定index位置的元素
4) int indexOf(Object obj):返回obj在集合中首次出现的位置
5) int lastIndexOf(Object obj):返回obj在当前集合中末次出现的位置

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

7) Object set(int index,Object ele):设置指定index位置的元素为ele ,
相当于是替换.
8) List subList(int fromlndex, int tolndex):返回从fromIndex到
tolndex位置的子集合

List的遍历方式:List的三种遍历方式 [也适合:ArrayList, LinkedList, Vector]

3、ArrayList的主意事项:

1)permits all elements, including null ,ArrayList可以加入null,并且多个

2) ArrayList是由数组来实现数据存储的
3)ArrayList基本等同于Vector,除了ArrayList是线程不安全(执行效率高),在多线程情况下,不建议使用ArrayList

底层原码分析:

1) ArrayList中维护了一个Object类型的数组elementData. [debug 看源码]transient Object[ elementData;transient Object[] elementData; //transient表示瞬间,短暂的,表示该属性不会被序列化
2)当创建ArrayList对象时,如果使用的是无参构造器,则初始elementData容量为0,第1次添加,则扩容elementData为10,如需要再次扩容,则扩容elementData为1.5倍。3)如果使用的是指定大小的构造器,则初始elementData容量为指定大小,如果需要扩容,
则直接扩容elementData为1.5倍。

4、Vector底层结构

 四、LinkedList底层结构:

1、LinkedList的全面说明

1)LinkedList底层实现了双向链表和双端队列特点

2)可以添加任意元素(元素可以重复),包括null

3)线程不安全,没有实现同步

主要结构

详细介绍:

双向列表示意图

Arraylist和LinkedList的比较:

//LinkedList(增删改查)
LinkedList linkedList = new LinkedList();
//增加,如果没有下表索引,就直接按顺序放在之后
for (int i = 0;i<=10;i++) {
linkedList.add("hello" + i);
}
System.out.println(linkedList); //删除,并返回删除元素的值
System.out.println( linkedList.remove(0));
linkedList.remove("hello1"); System.out.println(linkedList);
linkedList.add("hello0");
linkedList.add(9,"hello11");
System.out.println(linkedList); //查询链表中是否有这个元素,有为true,反之为false
System.out.println(linkedList.contains("hello5")); //修改,修改对应下标的值
linkedList.set(3,"helloset");
System.out.println(linkedList.contains("hello5"));

如何选择ArrayList和LinkedList:
1)如果我们改查的操作多,选择ArrayList

2)如果我们增删的操作多,选择LinkedList
3)一般来说,在程序中,80%-90%都是查询,因此大部分情况下会选择ArrayList

4)在一个项自中,根据业务灵活选择,也可能这样,一个模块使用的是ArrayList,另外一个模块是LinkedList,也就是说,要根据业务来进行选择

五、Set接口

基本介绍:

1)无序(添加和取出的顺序不一致),没有索引(每一次输出的顺序都是一样的。)

2)不允许重复元素,所以最多包含一个null

Set接口的常用方法
和List接口一样, Set接口也是Collection的子接口,因此,常用方法和Collection接口一样工
Set接口的遍历方式
同Collection的遍历方式一样,因为Set接口是Collection接口的子接口。

1.可以使用迭代器
2.增强for
3.不能使用索引的方式来获取.

 //Set
//set的输入和输出排列不同,但是多次输出的结果都是相同的
Set set = new HashSet<>();
for (int i = 0; i < 10; i++) {
set.add("set"+i);
}
set.add("set10");
System.out.println(set);
//增加
set.add("set11");
System.out.println(set);
//删除
System.out.println(set.remove("set0"));
System.out.println(set);
//查询set中是否有当前元素
System.out.println(set.contains("set1"));
//重复元素不会添加
System.out.println(set.add("set1"));
//允许有一个null值
System.out.println(set.add("null"));
System.out.println(set);

案例演示

1).set接口的实现类的对象(Set接口对象),不能存放重复的元素,可以添加一个null

2). set接口对象存放数据是无序(即添加的顺序和取出的顺序不一致)
3).注意:取出的顺序的顺序虽然不是添加的顺序,但是他的固定。

1、Set接口实现类-HashSet

说明:方法及使用和HashSet的类似

1) HashSet实现了Set接口
2) HashSet实际上是HashMap,看下源码.

3)可以存放null值,但是只能有一个null
4) HashSet不保证元素是有序的,取决于hash后,再确定索引的结果.

(即,不保证存放元素的顺序和取出顺序)

5)不能有重复元素/对象.(如果是保存在堆中的两个对象,则可以)

 HashSet底层机制:分析HashSet底层是HashMap, HashMap底层是(数组+链表+红黑树)

1):先获取元素的哈希值(hashCode方法)

2):对哈希值进行运算,得出一个索引值即为要存放在哈希表中的位置号

3):如果该位置上没有其他元素则直接存放,如果该位置上已经有其他元素,则需要进行equals(这个equals是在类中重写父类的方法,具体是满足什么条件就确定两个对象相等,由编程人员确定)判断,如果相等,则不再添加,如果不相等,则以链表的方式添加。

大概的图解如下:(简单模拟:数组+链表结构)

HashSet添加元素是如何实现的(hash()+ equals())

特点和注意事项:

HashSet底层实现过程和代码流程解释:

import java.util.HashSet;

@SuppressWarnings({"all"})
public class HashSetSource {
public static void main(String[] args) { HashSet hashSet = new HashSet();
hashSet.add("java");//到此位置,第1次add分析完毕.
hashSet.add("php");//到此位置,第2次add分析完毕
hashSet.add("java");
System.out.println("set=" + hashSet); /*
对HashSet 的源码解读
1. 执行 HashSet()
public HashSet() {
map = new HashMap<>();
}
2. 执行 add()
public boolean add(E e) {//e = "java"
return map.put(e, PRESENT)==null;//(static) PRESENT = new Object();
}
3.执行 put() , 该方法会执行 hash(key) 得到key对应的hash值 算法h = key.hashCode()) ^ (h >>> 16)
public V put(K key, V value) {//key = "java" value = PRESENT 共享
return putVal(hash(key), key, value, false, true);
}
4.执行 putVal
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i; //定义了辅助变量
//table 就是 HashMap 的一个数组,类型是 Node[]
//if 语句表示如果当前table 是null, 或者 大小=0
//就是第一次扩容,到16个空间.
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; //(1)根据key,得到hash 去计算该key应该存放到table表的哪个索引位置
//并把这个位置的对象,赋给 p
//(2)判断p 是否为null
//(2.1) 如果p 为null, 表示还没有存放元素, 就创建一个Node (key="java",value=PRESENT)
//(2.2) 就放在该位置 tab[i] = newNode(hash, key, value, null) if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
//一个开发技巧提示: 在需要局部变量(辅助变量)时候,在创建
Node<K,V> e; K k; //
//如果当前索引位置对应的链表的第一个元素和准备添加的key的hash值一样
//并且满足 下面两个条件之一:
//(1) 准备加入的key 和 p 指向的Node 结点的 key 是同一个对象
//(2) p 指向的Node 结点的 key 的equals() 和准备加入的key比较后相同
//就不能加入
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
//再判断 p 是不是一颗红黑树,
//如果是一颗红黑树,就调用 putTreeVal , 来进行添加
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {//如果table对应索引位置,已经是一个链表, 就使用for循环比较
//(1) 依次和该链表的每一个元素比较后,都不相同, 则加入到该链表的最后
// 注意在把元素添加到链表后,立即判断 该链表是否已经达到8个结点
// , 就调用 treeifyBin() 对当前这个链表进行树化(转成红黑树)
// 注意,在转成红黑树时,要进行判断, 判断条件
// if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY(64))
// resize();
// 如果上面条件成立,先table扩容.
// 只有上面条件不成立时,才进行转成红黑树
//(2) 依次和该链表的每一个元素比较过程中,如果有相同情况,就直接break for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD(8) - 1) // -1 for 1st
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
if (e != null) { // existing mapping for key
V oldValue = e.value;
if (!onlyIfAbsent || oldValue == null)
e.value = value;
afterNodeAccess(e);
return oldValue;
}
}
++modCount;
//size 就是我们每加入一个结点Node(k,v,h,next), size++
if (++size > threshold)
resize();//扩容
afterNodeInsertion(evict);
return null;
}
*/ }
}

HashSet的扩容和转成红黑树机制:

对HashSet添加机制的实践:

import java.util.HashSet;
import java.util.Objects; public class HashSetTest {
public static void main(String[] args) { HashSet<Employee> employees = new HashSet<>();
System.out.println(employees.add(new Employee("tom",24)));
System.out.println(employees);
System.out.println(employees.add(new Employee("tony",24)));
System.out.println(employees);
System.out.println(employees.add(new Employee("tom",24)));
System.out.println(employees); } } class Employee{
private String name;
private int age; public Employee(String name, int age) {
this.name = name;
this.age = age;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee employee = (Employee) o;
return age == employee.age && name.equals(employee.name);
} @Override
public int hashCode() {
//将hashcode设置成固定值,那么就会在一条链表中确定值是否相同
// return Objects.hash(200); //用name和age生成hashcode,生成的hashcode相同则为同一条链表,
// 注意:这里因为只有两个参数,如果还有其他参数,就会根据equals判断是否在
// 同一个链表中有相同元素,相同就不添加,不相同就添加
// hashcode不同则直接添加到其他的table中
return Objects.hash(name, age);
} @Override
public String toString() {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

import java.util.HashSet;
import java.util.Objects; public class HashSetTest1 {
public static void main(String[] args) { HashSet<Employee1> employee1s = new HashSet<>();
System.out.println( employee1s.add(new Employee1("tom",2000,new MyDate("1998.5.5"))));//T
System.out.println(employee1s);
//工资不同
System.out.println( employee1s.add(new Employee1("tom",2500,new MyDate("1998.5.5"))));//F
System.out.println(employee1s);
//加入的生日不同
System.out.println( employee1s.add(new Employee1("tom",2000,new MyDate("1998.5.6"))));//T
System.out.println(employee1s);
//加入的名字不同
System.out.println( employee1s.add(new Employee1("tony",2000,new MyDate("1998.5.5"))));//T
System.out.println(employee1s); }
} class Employee1{
private String name;
private double sal;
private MyDate birthday; public Employee1(String name, double sal, MyDate birthday) {
this.name = name;
this.sal = sal;
this.birthday = birthday;
} @Override
public String toString() {
return "Employee1{" +
"name='" + name + '\'' +
", sal=" + sal +
", birthday=" + birthday +
'}';
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Employee1 employee1 = (Employee1) o;
return Objects.equals(name, employee1.name) && Objects.equals(birthday, employee1.birthday);
} @Override
public int hashCode() {
return Objects.hash(name, birthday);
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public double getSal() {
return sal;
} public void setSal(double sal) {
this.sal = sal;
} public MyDate getBirthday() {
return birthday;
} public void setBirthday(MyDate birthday) {
this.birthday = birthday;
}
} class MyDate{
private String date; public MyDate(String date) {
//可以根据当前时间格式化为自己想要的时间格式(此处不用)
// public MyDate(Date date) {
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// String format = simpleDateFormat.format(date);
this.date = date;
} public String getDate() {
return date;
} public void setDate(String date) {
// SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
// String format = simpleDateFormat.format(date);
this.date = date;
} @Override
public String toString() {
return "MyDate{" +
"date=" + date +
'}';
} @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
MyDate myDate = (MyDate) o;
return Objects.equals(date, myDate.date);
} @Override
public int hashCode() {
return Objects.hash(date);
} }

集合、Collection、list、set、HashSet的更多相关文章

  1. 复习java基础第三天(集合:Collection、Set、HashSet、LinkedHashSet、TreeSet)

    一.Collection常用的方法: Java 集合可分为 Set.List 和 Map 三种体系: Set:无序.不可重复的集合. List:有序,可重复的集合. Map:具有映射关系的集合. Co ...

  2. Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例

    概要 这一章,我们对HashSet进行学习.我们先对HashSet有个整体认识,然后再学习它的源码,最后再通过实例来学会使用HashSet.内容包括:第1部分 HashSet介绍第2部分 HashSe ...

  3. 5、数组和集合--Collection、Map

    一.数组:同一个类型数据的集合,其实他也是一个容器 1.数组的好处:可以自动给数组中的元素从0开始编号,方便操作这些数据 2.数组的定义: 在Java中常见: 格式1:  类型 [] 数组名 = ne ...

  4. 【转】Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例--不错

    原文网址:http://www.cnblogs.com/skywang12345/p/3311252.html 概要 这一章,我们对HashSet进行学习.我们先对HashSet有个整体认识,然后再学 ...

  5. 转:深入Java集合学习系列:HashSet的实现原理

    0.参考文献 深入Java集合学习系列:HashSet的实现原理 1.HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特 ...

  6. 集合Collection总览

    前言 声明,本文使用的是JDK1.8 从今天开始正式去学习Java基础中最重要的东西--->集合 无论在开发中,在面试中这个知识点都是非常非常重要的,因此,我在此花费的时间也是很多,得参阅挺多的 ...

  7. 9:集合collection

    第一 集合框架的概述 集合类的由来:  对象用于封装特有数据,对象多了需要存储,如果对象的个数不确定,就使用集合容器进行存储.   集合和数组的区别: 数组虽然可以存储对象,但是长度是固定的:集合长度 ...

  8. java基础27 单例集合Collection及其常用方法

    1.集合 集合是存储对象数据的集合容器 1.1.集合比数组的优势 1.集合可以存储任意类型的数据,数组只能存储同一种数据类型的数据    2.集合的长度是变化的,数组的长度是固定的 1.2.数组:存储 ...

  9. 18_java之集合Collection

    01集合使用的回顾 *A:集合使用的回顾 *a.ArrayList集合存储5个int类型元素 public static void main(String[] args) { ArrayList< ...

  10. 深入Java集合学习系列:HashSet的实现原理

    1. HashSet概述: HashSet实现Set接口,由哈希表(实际上是一个HashMap实例)支持.它不保证set 的迭代顺序:特别是它不保证该顺序恒久不变.此类允许使用null元素. 2. H ...

随机推荐

  1. 【Warrior刷题笔记】剑指offer 6 24 35. 三道题,让你学会链表递归迭代辅助栈

    题目一 从尾到头打印链表 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/cong-wei-dao-tou-da-yin-lian-biao-l ...

  2. django 字段默认值

    default 表示在页面中默认选中状态的值 页面为 来自为知笔记(Wiz)

  3. Selenium_获取元素文本、属性值、尺寸(8)

    from selenium import webdriver driver = webdriver.Chrome() driver.maximize_window() driver.get(" ...

  4. hyperf 如何对AMQP消息进行手动消费?

    转发自白狼栈:查看原文 在使用 hyperf 官方自带的 AMQP 队列时你会发现,不需要我们再额外启动进程对消息进行消费.这是因为默认情况下,使用 @Consumer 注解时,hyperf 会为我们 ...

  5. C#进程调用FFmpeg操作音视频

    项目背景 因为公司需要对音视频做一些操作,比如说对系统用户的发音和背景视频进行合成,以及对多个音视频之间进行合成,还有就是在指定的源背景音频中按照对应的规则在视频的多少秒钟内插入一段客户发音等一些复杂 ...

  6. 《Flink SQL任务自动生成与提交》后续:修改flink源码实现kafka connector BatchMode

    目录 问题 思路 kafka参数问题 支持batchmode的问题 参数提交至kafkasource的问题 group by支持问题 实现 编译 测试 因为在一篇博文上看到介绍"汽车之家介绍 ...

  7. Android官方文档翻译 十四 3.2Supporting Different Screens

    Supporting Different Screens 支持不同的屏幕 This lesson teaches you to 这节课教给你 Create Different Layouts 创建不同 ...

  8. Metasploit生成木马入侵安卓手机

    开始 首先你需要一个Metasploit(废话) Linux: sudo apt install metasploit-framework Termux: 看这里 指令 sudo su //生成木马文 ...

  9. 【刷题-LeetCode】166 Fraction to Recurring Decimal

    Fraction to Recurring Decimal Given two integers representing the numerator and denominator of a fra ...

  10. pytest文档4-fixture之conftest.py

    用例1需要先登录,用例2不需要登录,用例3需要先登录.很显然这就无法用setup和teardown来实现了.fixture之conftest.py就是自定义测试用例的预置条件 1.firture相对于 ...