JAVA数组、String与集合详解

目录

1. 数组

1.1 初始化

1.2 遍历

1.3 数组执行原理

1.4 Arrays操作数组

2. String字符串

3. 集合

3.1 Collection

3.1.1 常用方法

3.1.2 分类

3.1.3 遍历方式

3.1.4 List

 3.1.5 Set

3.1.6 适用场景

 3.1.7 Collection总结

3.2 Collection其他操作 

3.2.1 并发修改异常

 3.2.2 可变参数

3.2.3 Collections工具类

3.2 Map

3.2.1 常用方法​编辑

 3.2.2 遍历方式

3.2.3 Map底层原理


1. 数组

一种数据结构,特点是不可变,有索引,有序。

1.1 初始化

  • 静态初始化:int[] nums=[1,2,3]
  • 动态初始化:int[] nums2 = new int[5]

1.2 遍历

  • for循环
  • 增强for循环

1.3 数组执行原理

JAVA是把编译后的字节码加载到Java虚拟机中执行的。

为了方便虚拟机执行程序,将虚拟机划分为方法区、堆、栈、本地方法栈、寄存器五块区域。重点关注方法区、堆、栈。

  • 方法区:字节码文件先加载到这里。
  • 堆:存储new出来的对象,并分配地址。
  • 栈:方法运行时进入的区域,变量在这个区域中。

1.4 Arrays操作数组

JAVA定义了Arrays工具类来操作数组。常用方法有:

  • Arrays.toString(); //将数组中的内容全部打印出来
  • Arrays.sort(); //数组排序

2. String字符串

表示字符串类型,属于引用类型,不属于基本类型。

双括号括起来的字符串,是不可变的,会存储在方法区的常量池中。

但new出来的String存储在堆中,且分配不同地址。

 常用方法

3. 集合

 大框架分为单列集合Collection和双列集合Map。

List元素有序,可重复,有索引。

Set元素无序,不可重复,无索引。

Map是无序,无索引的键值对。

3.1 Collection

3.1.1 常用方法

3.1.2 分类

List:元素有序,可重复,有索引

  • ArrayList:数组实现,查询快,没有同步,线程不安全
  • LinkedList:链表实现,增删快,没有同步,线程不安全
  • Vector:数组实现,同步,线程安全。Stack是Vector的实现类

Set:元素无序,不重复,无索引

  • HashSet:Hash表存储元素,LinkedHashSet维护元素插入顺序
  • TreeSet:二叉树实现,做内部排序

3.1.3 遍历方式

  • 迭代器遍历:Iterator<String> it = c.iterator();  t.hasNext();   it.next()
  • 增强for循环
  • forEach遍历,也就是Lambda表达式遍历。list.forEach(e->sout(e))

3.1.4 List

ArrayList底层原理: 基于数组结构实现。

数组长度固定,ArrayList可变原理:

  1. 利用无参构造器创建的集合,会在底层默认创建长度为0的数组。
  2. 添加第一个元素时,底层会创建一个新的长度为10的数组。
  3. 存满时,会扩容1.5倍。
  4. 如果一次添加多个元素,1.5倍放不下,会以实际内容为准

数组扩容并不是在原数组上扩容,而是创建一个新的数组,将原来数组内容复制过去。

遍历方式:可以使用普通For循环,外加上面三种方式。

 3.1.5 Set

Set具有与Collection完全一样的接口,因此没有任何额外的功能,不像前面有两个不同的List。实际上Set就是Collection,只 是行为不同。(这是继承与多态思想的典型应用:表现不同的行为。)Set不保存重复的元素。

(1)HashSet

底层原理:HashSet底层时基于Hash表实现的。

  • JDK8以前:Hash表=数组+链表
  • JDK8以后:Hash表=数组+链表+红黑树

所以说要想使用HashSet存储元素,此元素对象要有两个方法:一个是hashCode方法获取元素的hashCode值(哈希值);另一个是调用了元素的equals方法,用来比较新添加的元素和集合中已有的元素是否相同。  

  • 只有新添加元素的hashCode值和集合中以后元素的hashCode值相同、新添加的元素调用equals方法和集合中已有元素比较结果为true, 才认为元素重复。

  • 如果hashCode值相同,equals比较不同,则以链表的形式连接在数组的同一个索引为位置(如上图所示)

在JDK8开始后,为了提高性能,当链表的长度超过8时,就会把链表转换为红黑树,如下图所示:

(2)LinkedHashSet

底层原理:也是Hash表,多了双向链表来维护元素的存取顺序。

(3)TreeSet

底层原理:二叉树(红黑树数据结构)

有序,不重复,无索引。

排序时必须指定规则,tring类型的元素,或者Integer类型的元素,它们本身就具备排序规。

两种方式定义排序规则:

  • 让元素的类实现Comparable接口,重写compareTo方法
  • 在创建TreeSet集合时,通过构造方法传递Compartor比较器对象。
@Override
public int compareTo(Student o) {
    //this:表示将要添加进去的Student对象
    //o: 表示集合中已有的Student对象
    return this.age-o.age;
}




Set<Student> set = new TreeSet<>(((o1, o2) -> o1.getAge()- o2.getAge()));

3.1.6 适用场景

 3.1.7 Collection总结

3.2 Collection其他操作 

3.2.1 并发修改异常

在使用迭代器遍历集合时,可能存在并发修改异常。

List<String> list = new ArrayList<>();
list.add("王麻子");
list.add("小李子");
list.add("李爱花");
list.add("张全蛋");
list.add("晓李");
list.add("李玉刚");
System.out.println(list); // [王麻子, 小李子, 李爱花, 张全蛋, 晓李, 李玉刚]

//需求:找出集合中带"李"字的姓名,并从集合中删除
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String name = it.next();
    if(name.contains("李")){
        list.remove(name);
    }
}
System.out.println(list);

运行上面的代码,会出现下面的异常。这就是并发修改异常

为什么会出现这个异常呢?那是因为迭代器遍历机制,规定迭代器遍历集合的同时,不允许集合自己去增删元素,否则就会出现这个异常。

怎么解决?交给迭代器就行

//需求:找出集合中带"李"字的姓名,并从集合中删除
Iterator<String> it = list.iterator();
while(it.hasNext()){
    String name = it.next();
    if(name.contains("李")){
        //list.remove(name);
        it.remove(); //当前迭代器指向谁,就删除谁
    }
}

 3.2.2 可变参数

  • 可变参数是一种特殊的形式参数,定义在方法、构造器的形参列表处,它可以让方法接收多个同类型的实际参数。

  • 可变参数在方法内部,本质上是一个数组

public class ParamTest{
    public static void main(String[] args){
        //不传递参数,下面的nums长度则为0, 打印元素是[]
        test();	
        
        //传递3个参数,下面的nums长度为3,打印元素是[10, 20, 30]
        test(10,20,30); 
        
        //传递一个数组,下面数组长度为4,打印元素是[10,20,30,40] 
        int[] arr = new int[]{10,20,30,40}
        test(arr); 
    }
    
    public static void test(int...nums){
        //可变参数在方法内部,本质上是一个数组
        System.out.println(nums.length);
        System.out.println(Arrays.toString(nums));
        System.out.println("----------------");
    }
}

注意:

  • 一个形参列表中,只能有一个可变参数;否则会报错

  • 一个形参列表中如果多个参数,可变参数需要写在最后;否则会报错

3.2.3 Collections工具类

3.2 Map

 键不能重复,值可以重复,每一个键只能找到自己对应的值。

3.2.1 常用方法

 3.2.2 遍历方式

  • 键找值:keySet(),get()
  • 获取整个键值对:Set<Map.Entry<K,V>>  entrySet()
  • forEach+Lambda表达式:map.forEach((k,v) -> sout(k+":"v))

3.2.3 Map底层原理

其实Map和Set说白了底层是一个东西。都是基于Hash表实现的。

  • Map,HashMap,LinkedHashMap,TreeMap
  • Set,HashSet,LinkedHashSet,TreeSet

很像对吧

  • Hashxxx都是无序,不重复,无索引,基于HashTable实现
  • LinkedHashxxx都是有序,不重复,无索引,基于HashTable+链表实现
  • Treexxx都是按大小默认升序排序,不重复,无索引,基于红黑树实现

HashMap追溯到最底层构造方法里其实new了一个HashMap,只保留了键,省略了值。

所以Set和Map都是基于HashTable实现的。保证不重复的原理就是hashCode()和equals()。

HashMap底层数据结构: 哈希表结构
	JDK8之前的哈希表 = 数组+链表
	JDK8之后的哈希表 = 数组+链表+红黑树
	哈希表是一种增删改查数据,性能相对都较好的数据结构
	
往HashMap集合中键值对数据时,底层步骤如下
	第1步:当你第一次往HashMap集合中存储键值对时,底层会创建一个长度为16的数组
	第2步:把键然后将键和值封装成一个对象,叫做Entry对象
	第3步:再根据Entry对象的键计算hashCode值(和值无关)
	第4步:利用hashCode值和数组的长度做一个类似求余数的算法,会得到一个索引位置
	第5步:判断这个索引的位置是否为null,如果为null,就直接将这个Entry对象存储到这个索引位置
		   如果不为null,则还需要进行第6步的判断
	第6步:继续调用equals方法判断两个对象键是否相同
		  如果equals返回false,则以链表的形式往下挂
		  如果equals方法true,则认为键重复,此时新的键值对会替换就的键值对。
	
HashMap底层需要注意这几点:
	1.底层数组默认长度为16,如果数组中有超过12个位置已经存储了元素,则会对数组进行扩容2倍
	  数组扩容的加载因子是0.75,意思是:16*0.75=12 	
   	
    2.数组的同一个索引位置有多个元素、并且在8个元素以内(包括8),则以链表的形式存储
    	JDK7版本:链表采用头插法(新元素往链表的头部添加)
    	JDK8版本:链表采用尾插法(新元素我那个链表的尾部添加)
    	
    3.数组的同一个索引位置有多个元素、并且超过了8个,则以红黑树形式存储
    

 

 

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

NEUMaple

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值