JAVA基础篇——集合

十三、集合

1. 概述

存放数据的集合,底层是数组、链表或红黑树实现。因为应用场景的需求,Java提供了不同的接口以及实现类。

集合中存储的是元素的地址信息。

2. Collection

Collection是泛型接口,其集合体系如下图

JAVA基础篇——集合_第1张图片

有序:元素存入顺序和取出顺序一致

重复:可以放入相同元素在不同位置

索引:能否通过索引操作集合中的元素

public class CollectionTest1 {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();  // 有序,可重复,有索引
        list.add("1");
        list.add("2");
        list.add("1");
        list.add("2");
        System.out.println(list);

        HashSet<String> set = new HashSet<>();  // 无序,无重复,无索引
        set.add("1");
        set.add("2");
        set.add("1");
        System.out.println(set);
    }
}

2.1 Collection的常用方法:

public class CollectionTest1 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        // add()添加
        c.add("1");
        c.add("2");
        System.out.println(c);
        // clear()清空集合的元素
//        c.clear();
        System.out.println(c);
        // isEmpty()判断集合是否为空
        System.out.println(c.isEmpty());
        // size()获取集合大小
        System.out.println(c.size());
        // contains()判断集合中是否包含某个元素——精确匹配
        System.out.println(c.contains("1"));
        // remove()删除,如果重复,只删除第一个
        System.out.println(c.remove("1"));

        // toArray()把集合转化成数组,返回的是Object类型,因为泛型在后续就没有了,这样可以兼容任意类型的数据
        Object[] arr = c.toArray();
        System.out.println(Arrays.toString(arr));
        // 若想指定数组类型
        String[] arr2 = c.toArray(new String[c.size()]);
        System.out.println(Arrays.toString(arr2));

        // 把一个集合的全部数据倒入另一个集合中去——批量添加
        Collection<String> c1 = new ArrayList<>();
        c1.add("1");
        c1.add("2");
        Collection<String> c2 = new ArrayList<>();
        c2.add("3");
        c2.add("4");
        c1.addAll(c2);
        System.out.println(c1);  // [1, 2, 3, 4]
        System.out.println(c2);  // [3, 4]
    }
}

2.2Collection的遍历方式:

Collection不支持使用for循环进行遍历,因为for循环遍历的情况应该支持索引,但是Collection中的不一定都支持索引,比如Set。

分类:

  • 迭代器:集合遍历的专用方式,Iterator
  • 加强for:本质是迭代器遍历,是迭代器遍历的简化写法
  • Lambda表达式:Map的时候好处多多简洁多多
public class CollectionTest2 {
    public static void main(String[] args) {
        Collection<String> c = new ArrayList<>();
        c.add("1");
        c.add("2");
        c.add("3");
        c.add("4");
        c.add("5");
        System.out.println(c);

//        // 使用迭代器遍历,千万不能越界咯,越界会出现不存在元素的异常
//        Iterator it = c.iterator();
        System.out.println(it.next());
        System.out.println(it.next());
//        // hashNext()的底层是看指针是否等于集合的长度
//        while (it.hasNext()) {
//            // 如果重复使用这个元素的话,不记住这个元素就会存在问题
//            // 多次next他会一致往后走
//            // 询问一次走一次,变量记住比较合理
//            System.out.println(it.next());
//        }

//        // 增强for循环——可以遍历集合也可以遍历数组
//        // for (元素类型 变量名:数组或集合) {
//        //    }
//        for(String x: c) {
//            System.out.println(x);
//        }
//        String[] name = {"11", "22", "33"};
//        for (String x:name) {
//            System.out.println(x);
//        }

        // Lambda表达式
        // forEach()
        c.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });

        c.forEach(s-> System.out.println(s));

        c.forEach(System.out::println);
    }
}

3. List

特点: 有序、可重复、有索引

特有方法: 因为支持索引,所以多了很多与索引相关的方法

public class ListTest1 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();  // 经典代码
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("2");
        System.out.println(list);

        // 在某个位置插入元素。注意这里的index不能大于长度
        list.add(4, "4");
        System.out.println(list);

        // 根据索引删除元素,并返回被删除元素
        System.out.println(list.remove(0));
        System.out.println(list);

        // 获得某个索引位置处的值
        System.out.println(list.get(0));

        // 修改某个索引位置处的值
        System.out.println(list.set(0, "9"));
    }
}

遍历: 一共四种,增加了for循环

public class ListTest2 {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();  // 经典代码
        list.add("1");
        list.add("2");
        list.add("3");
        list.add("2");
        System.out.println(list);

        // for
        for (int i = 0; i < list.size(); i++) {
            String temp = list.get(i);
            System.out.println(temp);
        }

        // iterator
        Iterator it = list.iterator();
        while (it.hasNext()) {
            System.out.println(it.next());
        }

        // foreach
        for (String s : list) {
            System.out.println(s);
        }

        // lambda
        list.forEach(System.out::println);
    }
}

两个实现类: ArrayList、LinkedList——>底层实现不同,业务场景也不同

适合的应用场景:

ArrayList

public class ArrayListDemo1 {
    public static void main(String[] args) {
        // 1. 创建一个ArrayList的集合对象
        ArrayList list = new ArrayList();
        list.add("黑马");
        list.add(666);
        list.add('a');
        System.out.println(list);

        // 2. 约束集合存储的范围
        ArrayList<String> list1 = new ArrayList<String>(10);

        System.out.println(list1);
        ArrayList<String> list2 = new ArrayList<>();  // JDK1.7后支持
        list1.add("2");
        list1.add("xx");
//        System.out.println(list1[0]);  // 0没有这种中操作

        // 3. 在集合中的某个索引位置添加一个数据——这个位置处要有值
//        list1.add(3, "aaa");  // 报错,意思就是超过范围了
        System.out.println(list1);

        // 4. 根据索引获取集合中某个索引位置处的值
        System.out.println(list1.get(0));
        System.out.println(list1.size());

        // 5. 删除元素
        // 5.1 根据索引删除某个元素,并返回被删除的元素
        System.out.println(list1.remove(1));
        // 5.2 根据元素删除
        System.out.println(list1.remove("2"));

        // 尖括号中必须是引用数据类型,若要存入int、char等基本数据类型,要使用包装类
        ArrayList<Integer> list3 = new ArrayList<>();
        list3.add(1);
        list3.add(2);
        list3.add(3);
        System.out.println(list3.get(0));
        System.out.println(list3.remove(0));
//        list3.remove(2);  // 想要删除的是特定对象,是引用数据类型,跟元素的具体内容无关,而是需要地址一致
    }
}

LinkedList

public class LinkedListTest {
    public static void main(String[] args) {
        // 创建队列
        LinkedList<String> queue = new LinkedList<>();
        // 入队操作
        queue.addLast("1");
        queue.addLast("2");
        queue.addLast("3");
        queue.addLast("4");
        System.out.println(queue);
        // 出队操作
        System.out.println(queue.removeFirst());
        System.out.println(queue);

        // 创建栈
        LinkedList<String> stack = new LinkedList<>();
        // 压栈(push)
        stack.addFirst("1");  // pop
        stack.addFirst("2");
        stack.addFirst("3");
        stack.addFirst("4");
        stack.push("5");
        System.out.println(stack);  // [4, 3, 2, 1]——实现了倒序
        // 出栈(pop)
        System.out.println(stack.pop());
        System.out.println(stack.removeFirst());  // 4
        System.out.println(stack.removeFirst());  // 3
        System.out.println(stack);  // [2, 1]这里要非常注意
    }
}

4. Set

是一个接口

特点:——无序、不重复、无索引

实现类:

  • HashSet:无序、不重复、无索引
  • LinkedHashSet:有序、不重复、无索引
  • TreeSet:排序、不重复、无索引
public class SetTest1 {
    public static void main(String[] args) {
        // 创建一个对象
//        Set set = new HashSet<>();  // 创建一个HashSet的集合对象。经典代码  // [1, 2, 3, 4, 5]HashSet  [888, 777, 666, 555],根据底层原理,这里的结果可以解释为由哈希值计算出来的顺序决定,不是表面上看到的升序还是降序
//        Set set = new LinkedHashSet<>();  // 创建一个LinkedHashSet的集合对象  // [5, 2, 1, 3, 4]
        Set<Integer> set = new TreeSet<>();  // 创建一个TreeSet的集合对象  // [1, 2, 3, 4, 5]默认升序排序  [555, 666, 777, 888]
//        set.add(5);
//        set.add(2);
//        set.add(1);
//        set.add(3);
//        set.add(4);
//        set.add(1);
        set.add(666);
        set.add(555);
        set.add(555);
        set.add(888);
        set.add(888);
        set.add(777);
        set.add(777);
        System.out.println(set);

        // Set常营的方法,基本上是Collection提供的!!自己几乎没有新增方法
    }
}

4.1 HashSet

哈希值:

  • int类型的数值,Java中每个对象都有一个Hash值,可以使用Object类提供的hashCode()方法获取,返回对象自己的hash值
  • 特点:同一对象调用,返回值是相同的;不同的对象Hash值一般不同,但也可能相同,发生Hash碰撞——String类的hashCode()方法把字符值代入特定的公式计算哈希值,可以设计出哈希碰撞,实际中并不容易出现碰撞。

底层原理: 底层有索引,只不过外界用不了

基于哈希表实现——增删改查数据性能都较好的数据结构

JDK8之前,哈希表=数组+链表

  • 创建HashSet时,会创建一个默认长度是16的数组,默认加载因子为0.75,数组名为table——如果数组的长度超过16*0.75,系统自动扩容为原来的两倍
  • 增加一个数据会根据他的Hash值计算位置(无序)
  • 如果计算出来的位置是null,直接存入
  • 如果计算出来的位置里面已经存放了元素,使用equals比较元素的内容,如果相同就不存放了;如果不同,新元素占老元素的位置,老元素挂在下面
  • 问题: 数太多,链表过长,会导致查询性能降低

JDK8开始,哈希表=数组+链表+红黑树(可以子平衡的二叉树)

  • 当链表长度超过8,且数组长度大于等于64时,自动将链表转成红黑树
  • 与JDK8之前的不同,新元素的坑如果已经被占且值不相等时,是将新元素挂在老元素的下面,不会替换坑里面的内容

HashSet集合去重复的机制

HashSet集合默认不能对内容一样的两个不同对象去重复(两个内容一样的学生能够对象,不能去重复!)

想要去重复的话——必须重写对象的hashCode()和equals()方法

4.2 LinkedHahSet

有序、不重复、无索引

底层原理:

  • 依然是基于哈希表(数组、链表、红黑树)实现的
  • 但是,它的每个元素都额外增加了一个双链表的机制记录前后元素的位置
  • 通过占用内存来换取有序

4.3 TreeSet

排序(默认升序排序)、不重复、无索引

底层原理: 红黑树

注意:

排序数字和字符串都可以

自定义对象:①让自定义类实现Comparable接口,重写里面的CompareTo方法;②通过调用TreeSet集合有参构造器,设置Comparator对象,指定比较规则

使用集合的注意事项:集合的并发修改异常问题(ConcurrentModificationException)

并发:多件事进行

例如:遍历集合时,同时删除集合中的数据,程序就会出现并发修改异常的错误——使用迭代器的方法删除元素/for循环i–/for循环反向循环

增强for循环和Lambda表达式无法解决并发修改异常

5. Collections工具类

可变参数

  • 概念: 特殊形参,定义在方法、构造器的形参列表中,格式是:数据类型…参数名称
  • 特点: 参数0-多个/传一个数组
  • 本质: 可变参数在方法内部,本质上就是一个数组
  • 注意: 一个形参列表中,只有一个可变参数;可变参数必须放在形参列表的最后面

Collections

概念: 操作集合的工具类

常用静态方法:

public class CollectionsTest1 {
    public static void main(String[] args) {
        // 1. addAll(Collection c, T...elements)
        List<String> names = new ArrayList<>();
        Collections.addAll(names, "11", "22", "33");
        System.out.println(names);

        List<String> names2 = new ArrayList<>();
        names2.add("44");
        // names2.addAll("55", "66");  // 会报错!!可以添加对象,但不可以这样直接赋值,要使用Clllections工具类里面的addAll()方法
        System.out.println(names2);
        names.addAll(names2);
        System.out.println(names);

        // 2. shuffle(List list)打乱List集合中的元素顺序
        Collections.shuffle(names);
        System.out.println(names);

        // 3. sort(List list)对List集合中的元素进行升序排序
        List<Integer> list = new ArrayList<>();
        list.add(3);
        list.add(2);
        list.add(1);
        Collections.sort(list);
        System.out.println(list);

        // 4. 自定义类型的对象比较,重写Comparator()

    }
}

6. Map

双列集合,格式:{key1=value1, key2=value2, key3=value3……},一次需要存一对数据作为一个元素

“key=value”称为一个键值对/键值对对象/一个Entry对象,Map集合也被叫做“键值对集合”

Map集合的所有键不允许重复,值可以重复,键和值是一一对应的

应用场景:

需要存储一一对应的数据时,就可以使用Map集合

Map集合的体系(特点:特点都是由键决定的)

JAVA基础篇——集合_第2张图片

特点:

  • HashMap(由键决定):无序、不重复、无索引(用的最多)
  • LinkedHashMap:有序、不重复、无索引
  • TreeMap:按照大小默认升序排序、不重复、无索引
public class MapTest1 {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();  // 经典代码:无序、不重复、无索引  {null=null, 手表=220, Java=2, 手机=2}
//        Map map = new LinkedHashMap<>();  // 有序、不重复、无索引  {手表=220, 手机=2, Java=2, null=null}
//        Map map = new TreeMap<>();  // 可排序序、不重复、无索引 键是数值可排序的
        map.put("手表", 100);
        map.put("手表", 220);  // 键不可以重复,后面的会覆盖前面的
        map.put("手机", 2);
        map.put("Java", 2);  // 值可以重复
        map.put(null, null);  // 可以存放null值
        System.out.println(map);
    }
}

常用方法:

public class MapTest2 {
    public static void main(String[] args) {
        Map<String, Integer> map = new HashMap<>();  // 经典代码:无序、不重复、无索引  {null=null, 手表=220, Java=2, 手机=2}
        map.put("手表", 100);
        map.put("手表", 220);  // 键不可以重复,后面的会覆盖前面的
        map.put("手机", 2);
        map.put("Java", 2);  // 值可以重复
        map.put(null, null);  // 可以存放null值
        System.out.println(map);

        // size():获取集合的大小
        System.out.println(map.size());

        // clear():清空集合
//        map.clear();
//        System.out.println(map);

        // isEmpty():判断集合是否为空,为空返回true
//        System.out.println(map.isEmpty());

        // get(Object key):根据键获取对应值
        System.out.println(map.get("手机"));
        System.out.println(map.get(""));  // null

        // remove(Object key):根据键删除整个元素(会返回键的值)
        System.out.println(map.remove("手表"));
        System.out.println(map);

        // containsKey(Object key):判断是否包含某个键,包含返回true
        System.out.println(map.containsKey("手表"));
        System.out.println(map.containsKey("java"));  // 精确匹配

        // containsValue(Object value):判断是否包含某个值,包含返回true
        System.out.println(map.containsValue(2));

        // keySet():获取map集合全部键
        Set<String> set = map.keySet();
        System.out.println(set);

        // values():获取全部值,返回是Cllection集合,值可重复
        Collection<Integer> values = map.values();
        System.out.println(values);

        // 把其他map集合数据倒入自己map集合
        Map<String, Integer> map1 = new HashMap<>();
        map1.put("java1", 10);
        map1.put("java2", 20);
        Map<String, Integer> map2 = new HashMap<>();
        map2.put("java2", 30);
        map2.put("java3", 40);
        map1.putAll(map2);
        System.out.println(map1);  // {java3=40, java2=30, java1=10}
        System.out.println(map2);  // {java3=40, java2=30}
    }
}

Map集合的遍历方式

键找值:先获取Map集合全部键,再通过遍历键来找值

键值对:把“键值对”看成一个整体进行遍历

Lambda表达式:JDK1.8之后有的

public class MapTest3 {
    public static void main(String[] args) {
        // 准备一个map集合
        Map<String, Double> map = new HashMap<>();
        map.put("蜘蛛精", 166.3);
        map.put("蜘蛛精", 169.7);
        map.put("紫霞", 165.8);
        map.put("至尊宝", 169.5);
        map.put("牛魔王", 183.5);
        System.out.println(map);

        // 键找值遍历
        Set<String> keys = new HashSet<>();
        keys = map.keySet();
        System.out.println(keys);
        for (String key : keys) {
            double values = map.get(key);
            System.out.println(key + "====>" + values);
        }

        // 键值对entry对象
        Set<Map.Entry<String, Double>> entries = map.entrySet();
        for (Map.Entry<String, Double> entry : entries) {
            String name = entry.getKey();
            double height = entry.getValue();
            System.out.println(name + ":" + height);
        }

        // Lambda
        map.forEach((k, v) -> {
            System.out.println(k + "--->" + v);
        });
    }
}

6.1 HashMap

底层原理:

哈希表,Set集合的底层是基于Map实现的,只是Set集合中的元素只要键数据,不要值数据

HashMap的键依赖hashCode方法和equals方法保证键的唯一

6.2 LinkedHashMap

底层原理:

哈希表+双向链表

6.3 TreeMap

底层原理:

基于红黑叔实现的排序

7. 集合的嵌套

集合中的嵌套还是一个嵌套

你可能感兴趣的:(JAVA基础篇,java,开发语言)