Java数组和集合框架

数组

1.1 什么是数组

  • 数组是相同类型数据的有序集合
  • 数组描述的是相同类型的若干个数据,按照一定的先后次序排列组合而成。
  • 其中,每一个数据称为一个数组元素,每个数组元素都可以通过下标访问它们。

数组的四个基本特点

  • 数组的长度是确定的,一经创建,大小就不能再改变
  • 数组中的元素必须是同一类型数据
  • 数组中的元素可以是任意类型,基本数据类型或者引用数据类型
  • 数组变量属于引用类型,数组变量是在栈内存中的,存放堆内存中对应的数组的地址。 数组也可以看成是对象,数组中的每个元素相当于该对象的成员变量,数组本身就是对象,Java中对象是在堆中的,因此数组无论存放的是基本类型数据还是引用类型数据。数组对象本身是在堆内存中的。

数组边界

  • 下标的合法区间[0 ~ length-1]超出会报数组索引越界异常。(ArrayIndexOutOfBoundsException

1.2 数组声明和创建

  • 首先必须声明数组变量,才能在程序中使用数组,语法:
int[] arr; //建议使用
int arr[];
  • 可以使用new来创建数组
public class Demo6 {
    public static void main(String[] args) {
        int[] array; //声明数组
        array = new int[5]; //创建数组

        array[0] = 0;//为数组中的空间赋值,不赋值就是类型的默认值
        array[1] = 1;
        array[2] = 2;
        array[3] = 3;
        array[4] = 4;
        System.out.println(array.length);//获取数组长度
		//普通for循环
        for (int i = 0; i < array.length; i++) {
            System.out.println(i);
        }
		//增强for循环
        for(int x : array){
            System.out.println(x);
        }
    }
}
  • 数组的元素是通过索引来访问的,数组索引从0开始
  • 获取数组的长度,使用length属性 arr.length

1.3 Java内存分析

  • 存放new的对象和数组
  • 可以被所有的线程共享,不会存放别的对象引用

  • 存放基本变量类型(会包含这个基本类型的具体数值)
  • 引用对象的变量(会存放这个引用在堆里面的具体地址)

方法区

  • 可以被所有的线程共享
  • 包含了所有的class和static变量

在这里插入图片描述

1.4 初始化数组

静态初始化

int[] arr ={1,2,3,4};

动态初始化

int[] arr =new int[4];
arr[0]=1;
arr[1]=2;
arr[2]=3;
arr[3]=4;

默认初始化

  • 数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量同样的方式被隐式初始化。即int类型默认是0,引用数据类型默认是null。

1.5 数组使用

  • for each循环
  • 数组作方法参数
  • 数组作返回值
public class Demo {
    public static void main(String[] args) {
        int[] array = {1,2,3,4,4};

        //求数组中的所有数总和
        int sum = 0;
        for (int i = 0; i < array.length; i++) {
            sum +=array[i];
        }
        System.out.println("sum="+sum);

        //求数组中最大的数
        int max = array[0];
        for (int i = 1; i < array.length; i++) {
            if(max<array[i]){
                max=array[i];
            }
        }
        System.out.println("max="+max);
    }
}

1.6 多维数组

二维数组
数组里面存放的不是数据,而是另一个数组,数组名是array[i]
里面的数组中存放的是数据,用array[i][j]来获取数据。

public class Demo1 {
    public static void main(String[] args) {
        //创建一个二维数组
        int[][] arrays = {{1,2},{2,3},{3,4},{4,5}};
        //相当于四行二列的表格
        /*[4][2]
			1 2  array[0]
			2 3  array[1]
			3 4  array[2]
			4 5  array[3]
		*/

        //遍历这个二维数组的所有元素
        for (int i = 0; i < arrays.length; i++) {
            for (int j = 0; j < arrays[i].length; j++) {
                System.out.println(arrays[i][j]);
            }
        }
    }
}

在这里插入图片描述

1.7 数组工具类Arrays

java.util.Arrays

  • 由于数组对象本身并没有什么方法可供调用,但API中提供了一个工具类Arrays可以使用,可以对数据对象进行一些基本的操作
  • Arrays类中的方法都是static修饰的静态方法,在使用的时候可以直接通过类名来调用,而不用使用对象来调用(注意是‘不用’而不是‘不能’
  • 有以下一些常用功能
  1. 给数组赋值,通过fill方法
  2. 对数组排序,使用sort方法,按照升序来排序
  3. 比较数组,通过equals方法比较数组中的元素是否相等
  4. 查找数组元素,通过binarySearch方法能对排序好的数组进行二分查找法操作

1.7.1 fill()方法

 static void fill(boolean[] a, int fromIndex, int toIndex, boolean val)  

boolean[] a:需要填充的数组
int fromIndex:开始的数组索引
int toIndex:结束的数组索引(不包含)
boolean val:需要填充的值

public class Demo2 {

    public static void main(String[] args) {
        int[] a = {1,2,3,4,5};
        //将第2,3个元素填充为0
        Arrays.fill(a,1,3,0);
        System.out.println(Arrays.toString(a)); //[1, 0, 0, 4, 5]
    }
}

1.7.2 binarySearch()方法

 public static int binarySearch(int[] a,int fromIndex, int toIndex,int key)

参数 :

a - 要搜索的数组
fromIndex - 要搜索的第一个元素(包括)的索引
toIndex - 要搜索的最后一个元素(排他)的索引
key - 要搜索的值

public class Demo3 {
    public static void main(String[] args) {
        int[] a = {0,224,335,895,2,55,111};

        Arrays.sort(a);
        System.out.println(Arrays.toString(a)); //[0, 2, 55, 111, 224, 335, 895]

        int i = Arrays.binarySearch(a,0,7,895);

        System.out.println(i); //6 返回该数第一次出现所在的索引位置
    }
}

冒泡排序

public class Demo4 {
    public static void main(String[] args) {
        int[] a = {1,6,2,4,8,7,55};
        int[] sort = sort(a);
        System.out.println(Arrays.toString(sort));
    }

    public static int[] sort(int[] a){

        //外层循环一共需要a.length-1趟
        //内层循环需要a.length-1-i次
        for (int i = 0; i < a.length-1; i++) {
            boolean flag = false;
            for (int j = 0; j < a.length-1-i; j++) {
                if(a[j+1]<a[j]){
                    int temp = a[j+1];
                    a[j+1] = a[j];
                    a[j] = temp;
                    flag = true;
                }
            }
            //如果前后两个数已经比较过了,那么就不用再进入内部循环
            if(flag){
                break;
            }
        }
        return a;
    }
}

1.8 稀疏数组

当一个数组中大部分元素为0,或者为同一值时,可以使用稀疏数组来保存该数组

稀疏数组的处理方式

  • 记录数组一共有几行几列,有多少个不同值
  • 把具有不同值的元素和行列及值记录在一个小的数组中,从而缩小程序的规模

在这里插入图片描述

集合

2.1 什么是集合

  • 对象的容器,定义了对多个对象进行操作的常用方法,可实现数组的功能。

和数组的区别

  1. 数组长度固定,集合长度不固定
  2. 数组可以存储基本类型和引用类型,集合只能存储引用类型

java.util.*

在这里插入图片描述

2.2 Collection接口

在这里插入图片描述

2.3 List接口与实现类

2.3.1 List接口

特点:

  1. 有序的集合,存储元素和取出元素的顺序是一样的(存储123,取出123)
  2. 有索引,包含了一些带索引的方法
  3. 允许存储重复的元素

List 接口提供了 4 种对列表元素进行定位(索引)访问方法(特有)

  1. void add(int index, Object o) 在列表的指定位置插入指定元素(可选操作)。
  2. Object get(int index) 返回列表中指定位置的元素。
  3. Object remove(int index) 移除列表中指定位置的元素(可选操作)。
  4. Object set(int index, Object o) 用指定元素替换列表中指定位置的元素(可选操作)。
  5. List subList(int fromIndex,int toIndex)返回下标在fromIndex和toIndex之间的集合元素
  6. ListIterator<?> listIterator() 返回列表中的列表迭代器(按适当的顺序)。
  7. Boolean contains(Object o) 判断列表中是否包含该对象,包含返回true,否则false
  8. Boolean isEmpty()判断该列表是否为空,为空返回true
  9. int indexOf(Object o) 返回该元素在列表中的位置
  10. toArray() 将集合转换为数组

注意:

  • 使用索引的时候,一定要防止索引越界异常
  • 使用迭代器的时候,迭代器内部不能再使用Collection中的方法(并发操作异常)
  • 当向集合中添加基本类型数据时,存在一个自动装箱操作
  • 如果想要通过new一个在列表中已经存在的对象删除该对象,需要重写equals方法,只要两个对象的所有属性都相同,就认为这两个对象是同一对象。
public class InterfaceList {
   public static void main(String[] args) {
       //使用实现类ArrayList创建一个集合(多态)
       List<String> list = new ArrayList<>();

       list.add("迪丽热巴");
       list.add("古力娜扎");
       list.add("玛玛哈哈");

       System.out.println(list);//列表输出不是一个哈希值,说明重写了toString方法

       //1. void add(int index, E element) 在列表的指定位置插入指定元素(可选操作)。
       list.add(0,"玛尔扎哈");//在索引位置为0的地方添加一个元素
       System.out.println(list);

       //2. E get(int index) 返回列表中指定位置的元素。
       String s = list.get(2);
       System.out.println("索引为2的元素为:" + s);

       // 3.E remove(int index) 移除列表中指定位置的元素(可选操作)。返回值是移除的元素
       String s1 = list.remove(0);
       System.out.println("移除索引为0的元素,该元素为:" + s1);

       //4. E set(int index, E element) 用指定元素替换列表中指定位置的元素(可选操作)。
       String s2 = list.set(2, "撒由那拉");
       System.out.println("被替换的元素为:" + s2);

       /*List集合的三种遍历方式*/
       //使用普通的for循环
       for (int i = 0; i < list.size(); i++) {
           //public E get(int index):返回集合中指定位置的元素
           String s3 = list.get(i);
           System.out.println(s3);
       }
  
       /*使用迭代器*/
       Iterator<String> it = list.iterator();
       while (it1.hasNext()){
           String s4 = it.next();
           System.out.println(s4);
       }

       /*使用简化版的迭代器,增强for循环*/
       for(String str :list){
           System.out.println(str);
       }
      /*列表迭代器,ListIterator可以向前或者向后遍历遍历过程中允许添加,删除,修改元素*/
       ListIterator<String> LI = list.listIterator();
       
       //先从前往后迭代,指针指向最后一个位置
       while (LI.hasNext()){
           System.out.println(LI.nextIndex()+":"+LI.next());
       }

       //此时指针指向最后一个位置,从后往前迭代
       while (LI.hasPrevious()){
           System.out.println(LI.previousIndex()+":"+LI.previous());
       }
   }
}

List存放基本数据类型

public class Demo2 {
    public static void main(String[] args) {
        List list = new ArrayList();

        //集合存放基本数据类型存在一个自动装箱操作
        list.add(10);
        list.add(20);
        list.add(30);
        list.add(40);
        list.add(50);

        //删除元素,如果参数是int类型,表示的是下标
        //list.remove(20);索引越界异常
        list.remove((new Integer(20)));
        System.out.println(list); //[10, 30, 40, 50]

        //sublist方法,左闭右开[)
        List list1 = list.subList(0, 3);
        System.out.println(list1);//[10, 30, 40]
    }
}

2.3.2 List实现类

ArrayList

  • 必须获取一个连续空间,数组结构实现,查询快,增删慢。中间插入一个元素,之后的元素都要往后移。
  • 运行效率快,线程不安全

Vector

  • 数组结构实现,查询快,增删慢
  • 运行效率慢,线程安全

LinkedList

  • 无须获取连续空间,链表结构实现,增删快,查询慢

ArrayList源码分析

 DEFAULT_CAPACITY = 10 默认容量 

如果没有向集合中添加任何元素,默认容量是0,添加一个元素之后,容量是10,每次扩容0.5倍 new = old +(old>>2)

add()方法

//自动扩容核心代码
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    elementData = Arrays.copyOf(elementData, newCapacity);
}

LinkedList基本使用

public class Demo3 {
    public static void main(String[] args) {
        LinkedList list = new LinkedList();

        //添加数据
        list.add("苹果");
        list.add("香蕉");
        list.add("橘子");
        list.add("芒果");
        list.add("草莓");

        //删除数据
        list.remove("苹果");
        System.out.println(list);//[香蕉, 橘子, 芒果, 草莓]

        //遍历数据
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //判断
        System.out.println(list.contains("香蕉"));//true
        System.out.println(list.isEmpty());//false

        //获取
        System.out.println(list.indexOf("草莓"));//3
    }
}

在这里插入图片描述

2.4 泛型和工具类

  • 泛型是JDK1.5之后的新特性,本质是参数化类型,把类型作为参数传递
  • 泛型类,泛型接口,泛型方法
  • 提高代码的重用性,防止类型转换异常,提高代码安全性

自定义一个泛型的类

/* 创建一个泛型的类 */
public class Generic<E> {
    private E name;

    public E getName() {
        return name;
    }

    public void setName(E name) {
        this.name = name;
    }
}

根据该类创建对象

  • 自定义一个含有泛型的类,模拟ArrayList集合
  • 泛型是一个未知的数据类型,当我们不确定使用什么类型的数据时,可以使用泛型、
  • 泛型可以接受任意类型的数据,可以使用Integer,String,Student…
  • 创建对象的时候确定泛型的数据类型
public class Main {
    public static void main(String[] args) {
        //创建一个String类型的对象
        Generic<String> sc = new Generic<>();
        sc.setName("迪丽热巴");
        String s = sc.getName();
        System.out.println("s:" + s);

        //创建一个Integer类型的对象
        Generic<Integer> sc1 = new Generic<>();
        sc1.setName(2);
        Integer n = sc1.getName();
        System.out.println("n:" + n);
    }
}

定义一个泛型接口

/* 定义一个泛型的接口 */
public interface GenericInterface <I>{
    public abstract  void show(I i);
}

含有泛型接口的两种使用方式

/*
* 含有泛型的接口,第一种使用方式:定义接口的实现类,实现接口,指定接口的泛型
*
*   用Scanner类举例
*   public interface Iterator<E>{
*       E next();
*   }
* Scanner类实现了Iterator接口,并指定接口的泛型为String,重写的next方法泛型默认是String
*   public final class Scanner implements Iterator<String>{
*       public String next(){...}
*   }
* */

public class GenericInterfaceImpl1 implements GenericInterface<String> {
    @Override
    public void show(String s) {
        System.out.println(s);
    }
}
==============================================================================
/*
含有泛型的接口第二种使用方式,接口使用什么泛型,实现类就使用什么泛型,类跟着接口走
就相当于定义了一个含有泛型的类,创建对象的时候确定泛型的类型

    用ArrayList来举例,ArrayList类实现了List的接口
    public interface List<E>{
        boolean add(E e);
        E get(int index);
    }
    public class ArrayList<E> implements List<E>{
        public boolean add(E e){...}
        public E get(int index){...}
    }
*/


public class GenericInterfaceImpl2<I> implements GenericInterface<I> {
    @Override
    public void show(I i) {
        System.out.println(i);
    }
==============================================================================
public class Main {
    public static void main(String[] args) {
        GenericInterface i = new GenericInterfaceImpl1();
        i.show("Method");

        GenericInterface<Integer> i2 = new GenericInterfaceImpl2();
        i2.show(22);

        GenericInterface<String> i3 = new GenericInterfaceImpl2();
        i3.show("SSS");
    }
}

自定义一个泛型方法

 定义含有泛型的方法:泛型定义在修饰符和返回值类型之间

格式:

修饰符  <泛型> 返回值类型 方法名(参数列表(使用泛型)){
         方法体;
  }
  • 含有泛型的方法,在调用方法的时候确定泛型的数据类型
  • 传递什么类型的参数,泛型就是什么类型
public class MethodGeneric {
    //泛型的代表字母可以任意
    public static  <M> void show(M m){
        System.out.println(m);
    }
}
==================================================

public class Main {
    public static void main(String[] args) {
        //使用泛型方法
        MethodGeneric.show("张三");
        MethodGeneric.show(2);
        MethodGeneric.show(true);
    }
}

泛型的通配符

 ?:代表任意的数据类型
  • 不能创建对象使用,只能作为方法的参数使用
public class WildCard {
    public static void main(String[] args) {
        ArrayList<Integer> i1 = new ArrayList<>();
        i1.add(1);
        i1.add(2);
        printArray(i1);

        ArrayList<String> i2 = new ArrayList<>();
        i2.add("a");
        i2.add("b");
        printArray(i2);
    }

    //创建一个方法,无论参数是什么数据类型,都能将集合中的元素遍历输出
    //注意:
        //泛型没有继承概念的,因此参数ArrayList<?>不能改为ArrayList<Object>
    public static void printArray(ArrayList<?> list){
        Iterator<?> it = list.iterator();
        while(it.hasNext()){
            //it.next()方法,取出的元素是Object,可以接受任意的数据类型
            System.out.println(it.next());

        }
    }
}

泛型集合

  • 参数化类型、类型安全的集合,强制集合元素的类型必须一致

特点

  • 编译时即时检查,而非运行时抛出异常
  • 访问时,不必类型转换
  • 不同泛型之间引用不能相互赋值,泛型不存在多态
public class Demo1 {
    public static void main(String[] args) {
        ArrayList<Integer> list = new ArrayList<>();
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);
        //list.add("1");编译时就会报错

        Iterator<Integer> iterator = list.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());//不用再类型转换
        }
    }
}

2.5 Set接口和实现类

2.5.1 Set接口

  • 特点:无序,无下标,元素不可重复
  • 方法:全部继承Collection接口的方法

2.5.2 Set实现类

HashSet

  • 基于HashCode实现元素不重复
  • 当存入元素的哈希码相同时,会调用equals确认,如结果为true,拒绝后者存入
  • 存储结构:(数组+链表)JDK1.8之后:(数组+链表+红黑树)

TreeSet

  • 基于排列顺序实现元素不重复
  • 实现了SortedSet接口,对集合元素自动排序
  • 元素对象的类型必须实现Comparable接口,指定排序规则
  • 通过CompareTo方法确定是否为重复元素,该方法返回值是0,表示是重复元素
  • 存储结构:红黑树

HashSet基本使用

public class Demo1 {
    public static void main(String[] args) {
        HashSet<String> set = new HashSet<>();
        //1.添加
        set.add("小米");
        set.add("苹果");
        set.add("华为");
        set.add("苹果"); //重复数据添加不进去
        System.out.println(set); //[苹果, 华为, 小米]
        System.out.println("总元素个数"+set.size()); //3

        //2.删除
        set.remove("小米");
        System.out.println(set); //[苹果, 华为]

        //3.遍历
        //使用增强for循环
        for (String s : set) {
            System.out.println(s);
        }
        //使用迭代器
        Iterator<String> iterator = set.iterator();
        while (iterator.hasNext()){
            System.out.println(iterator.next());
        }

        //4.判断
        System.out.println(set.contains("小米")); //false
        System.out.println(set.isEmpty()); //false

        //5.set集合没有下标,无法获取索引
    }
}

HashCode存储过程(重复依据)

  • 根据hashcode计算保存的位置,如果此位置为空,则直接保存,如果不为空执行下一步
  • 执行equals方法,如果equals方法为true,认为重复数据,否则,形成链表

如果不重写hashcode和equals方法
set.add(new Student(“小新”,5));可以被成功添加,因为hashcode不同。

public class Demo2 {
    public static void main(String[] args) {
        Student s1 = new Student("小新",5);
        Student s2 = new Student("风间",4);
        Student s3 = new Student("阿呆",6);

        HashSet<Student> set = new HashSet<>();

        set.add(s1);
        set.add(s2);
        set.add(s3);
        set.add(new Student("小新",5));

        System.out.println(set.size());//4
    }
}

重写hashcode和equals之后,姓名和年龄都相同就被认为是同一个对象,不能被重复添加到set集合中。

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Student student = (Student) o;
        return age == student.age &&
                Objects.equals(name, student.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
查看hash方法的源码

为什么要用31

  1. 31是一个质数,减少散列冲突
  2. 提高执行效率 31* result = (result<<5)-result (在计算机中,位运算是效率最高的),左移5位表示result*25
 public static int hashCode(Object a[]) {
        if (a == null)
            return 0;
        int result = 1;
        for (Object element : a)
            result = 31 * result + (element == null ? 0 : element.hashCode());
        return result;
    }

treeSet

存入的数据必须要实现Comparable<?>接口,并且规定排序规则(实现compareTo方法)
java.lang.ClassCastException: 类型转换异常

重写compareTo方法,定义排序规则

   @Override
    public int compareTo(Student s) {
        int a = this.getName().compareTo(s.getName());
        int b = this.age - s.age;
        //先比较姓名,如果姓名相同,比较年龄,按照年龄升序排列
        return a==0 ? b:a ;
    }

实现Comparable接口,并且重写compareTo方法之后

public class Demo1 {
    public static void main(String[] args) {
        Student s1 = new Student("a小新",5);
        Student s4 = new Student("a小新",4);

        Student s2 = new Student("a风间",4);
        Student s3 = new Student("c阿呆",6);

        TreeSet<Student> treeSet = new TreeSet<>();
        treeSet.add(s1);
        treeSet.add(s2);
        treeSet.add(s3);
        treeSet.add(s4);

        for (Student student : treeSet) {
            System.out.println(student);
        }
    }
}
			/*
			Student{name='a小新', age=4}
			Student{name='a小新', age=5}
			Student{name='a风间', age=4}
			Student{name='c阿呆', age=6}
			*/

存储对象的类不实现Comparable接口

  • 在创建的时候就指定比较规则,使用匿名内部类
public class Demo2 {
    public static void main(String[] args) {
        Student s1 = new Student("a小新",5);
        Student s4 = new Student("a小新",4);
        Student s2 = new Student("a风间",4);
        Student s3 = new Student("c阿呆",6);

        TreeSet<Student> treeSet = new TreeSet<>(new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                //先按年龄,再按姓名
                int a = o1.getAge()-o2.getAge();
                int b = o1.getName().compareTo(o2.getName());
                return a==0?b:a ;
            }
        });

        treeSet.add(s1);
        treeSet.add(s2);
        treeSet.add(s3);
        treeSet.add(s4);

        System.out.println(treeSet);
    }
}
			/*
			Student{name='a小新', age=4}
			Student{name='a风间', age=4}
			Student{name='a小新', age=5}
			Student{name='c阿呆', age=6}
			*/

使用lambda表达式简化

    TreeSet<Student> treeSet = new TreeSet<Student>(( o1,  o2) ->
                 o1.getAge()-o2.getAge()==0?
                 o1.getName().compareTo(o2.getName()):
                 o1.getAge()-o2.getAge()
        );

treeSet练习
使用treeSet按照字符串长度进行排序存储,如果长度相同,按照字符串顺序排序

public class Demo3 {
    public static void main(String[] args) {
        //使用treeSet按照字符串长度进行排序存储,如果长度相同,按照字符串顺序排序

        TreeSet<String> treeSet = new TreeSet<>(
        (o1,o2)->o1.length()-o2.length()==0?
        o1.compareTo(o2):
        o1.length()-o2.length());

        treeSet.add("asdf");
        treeSet.add("asdfeaf");
        treeSet.add("asdfa");
        treeSet.add("asdd");
        treeSet.add("asdfdsaf");

        for (String s : treeSet) {
            System.out.println(s);
        }
    }
}

2.6 Map接口和实现类

2.6.1 Map接口

在这里插入图片描述

  • 用于存储键值对(K,V),键不能重复,值可以重复,无序集合

方法

  • V put(K key,V value),将对象存储到集合中,key重复则覆盖原值
  • Object get(K key),根据键获取对应的值
  • Set< K > keySet(),返回所有的key,返回值是一个set集合,不能重复
  • Collection< V > values(),返回所有的值,返回值是一个集合,可以重复
  • Set<Map.Entry<K,V>> entrySet() ,返回键值匹配的set集合 ,不能重复
public class Demo1 {
    public static void main(String[] args) {
        Map<Integer,String> map = new HashMap<>();

        map.put(1,"小新");
        map.put(5,"小葵");
        map.put(8,"风间");
        map.put(4,"阿呆");
        map.put(2,"正南");

        //遍历map集合,获取key。根据key获取value
        Set<Integer> keySet = map.keySet();
        for (Integer key : keySet) {
            System.out.println(key + "::"+map.get(key));
        }

        //获取所有的value
        Collection<String> values = map.values();
        for (String value : values) {
            System.out.println(value);
        }

        //根据键获取对应的值
        String s = map.get(8);
        System.out.println(s);

        //获取所有的键值对集合
        Set<Map.Entry<Integer, String>> entries = map.entrySet();
        for (Map.Entry<Integer, String> entry : entries) {
            Integer key = entry.getKey();
            String value = entry.getValue();
            System.out.println(key+":"+value);
        }
        
        //判断集合中是否有key或者value
 		System.out.println(map.containsKey(6)); //false
        System.out.println(map.containsValue("小白")); //false
    }
}

对于遍历map集合的两种方法,entryset方法效率高于keyset方法

2.6.2 Map实现类

hashMap

  • JDK1.2,线程不安全,运行效率快;允许用null作为key或者是value。
  • 默认容量16.ArrayList默认容量是10.
  • 默认加载因子0.75。当存放的数据超过总容量的0.75时,开始自动扩容
  • 存储结构:哈希表(数组+链表+红黑树)
  • 使用key的hashcode和equals方法来比较两个对象是否是同一对象。
  • JDK1.8新特性:当链表长度大于8,并且数组长度大于64的时候,链表结构自动转换为红黑树结构

源码

static final int DEFAULT_INITIAL_CAPACITY = 1 << 4;  //初始容量大小
static final int MAXIMUM_CAPACITY = 1 << 30;  //数组最大容量
static final float DEFAULT_LOAD_FACTOR = 0.75f;  //默认加载因子
static final int TREEIFY_THRESHOLD = 8;  //链表长度大于8时,调整为红黑树
static final int UNTREEIFY_THRESHOLD = 6;  //链表长度小于6,调整成链表结构
//当链表长度大于8,并且集合元素个数大于64时,调整为红黑树
static final int MIN_TREEIFY_CAPACITY = 64; 
transient Node<K,V>[] table;  //哈希表中的数值
transient int size;  //元素个数
刚创建好HashMap之后,table=null; size=0.目的是节省空间
添加第一个元素之后,数组的容量变为16.
当数组的有效元素个数超过16*0.75=12时,数组自动扩容,长度>>1即每次扩容为前一次的2倍。
JDK1.8之前是头插入,链表中的数据替换数组中的元素,数组中的元素后移
JDK1.8之后是尾插入,数组中的元素不变,新的元素依次跟在数组元素后面,形成链表

hashMap和hashSet的关系

  • HashSet的构造方法,就是创建一个HashMap
  • HashSet的add方法,调用的是HashMap中的put方法
//HashSet的构造方法,就是创建一个HashMap
  public HashSet() {
        map = new HashMap<>();
    }
===========================================
//HashSet的add方法,调用的是HashMap中的put方法
  public boolean add(E e) {
        return map.put(e, PRESENT)==null;
    }

2.7 properties类

hashTable类

  • JDK1.0版本,线程安全,运行效率慢,不允许null作为key或者value(基本不用)

properties类

  • hashTable的子类,要求key和value都是String类型,通常用于配置文件的读取

2.8 treeMap类

  • 实现了SortedMap接口(是Map的子接口),可以对key自动排序
public class Demo1 {
    public static void main(String[] args) {
        Student s1 = new Student("a小新",5);
        Student s4 = new Student("a小新",4);
        Student s2 = new Student("a风间",4);
        Student s3 = new Student("c阿呆",6);

        //创建一个TreeMap集合,定义排序规则
        TreeMap<Student,Integer> treeMap = new TreeMap<Student,Integer>((
        (o1, o2) ->
            o1.getName().compareTo(o2.getName()) == 0 ? 
            o1.getAge()- o2.getAge():
            o1.getName().compareTo(o2.getName())
        ));

        //添加数据
        treeMap.put(s1,1);
        treeMap.put(s2,4);
        treeMap.put(s3,2);
        treeMap.put(s4,3);

        //获取数据
        Set<Map.Entry<Student, Integer>> entries = treeMap.entrySet();
        for (Map.Entry<Student, Integer> entry : entries) {
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
    }
}

treeSet和treeMap的关系

  • treeSet的构造方法,就是创建一个treeMap
  • treeSet的add方法,调用的是treeMap中的put方法
 public TreeSet(Comparator<? super E> comparator) {
        this(new TreeMap<>(comparator));
    }
=====================================================
    public boolean add(E e) {
        return m.put(e, PRESENT)==null;
    }

2.9 Collection工具类

  • 集合工具类,定义了一些除了存取之外的集合其他常用方法

在这里插入图片描述

public class Demo1 {
    public static void main(String[] args) {
        //集合的工具类
        ArrayList<String> list = new ArrayList<>();
        list.add("1鼠");
        list.add("4兔");
        list.add("3虎");
        list.add("5龙");
        list.add("2牛");

        //sort排序
        Collections.sort(list);
        System.out.println(list); //[1鼠, 2牛, 3虎, 4兔, 5龙]

        //binarySearch二分法查找元素所在的位置
        int i = Collections.binarySearch(list, "2牛");
        System.out.println(i); //1 返回索引,如果找不到,返回负数

        //copy复制
        ArrayList<String> list2 = new ArrayList<>();
        for (int i1 = 0; i1 < list.size(); i1++) {
            list2.add("a");
        }
        Collections.copy(list2,list);
        //被复制的数组长度不能比原数组长度短,否则会抛出资源不匹配异常
        System.out.println(list2);

        //reverse反转
        Collections.reverse(list);
        System.out.println(list);//[5龙, 4兔, 3虎, 2牛, 1鼠]

        //shuffle打乱
        Collections.shuffle(list);
        System.out.println(list);//[3虎, 4兔, 1鼠, 5龙, 2牛],每次运行结果都不同
        
        //集合转换为数组
         String[] array = list.toArray(new String[5]);
        System.out.println(Arrays.toString(array));
        
        //数组转换为集合(转换后的集合是一个受限集合,不能添加和删除)
        String[] names = {"a","b","c"};
        List<String> strings = Arrays.asList(names);
        System.out.println(strings);
        
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值