从零开始的Java集合

集合/容器

1.集合的概念

当我们需要保存一组类型相同的元素时,可以通过使用数组来存储,数组就是一个容器。但是数组有一个缺点:数组一旦定义,长度将不能再变化。

在实践中,我们需要保存一些变长的数据集合,需要保存一些能够动态增长长度的容器来存储数据,同时我们需要对数据的保存的逻辑可能各种各样,于是就有了各种各样的数据结构。Java中对于各种数据结构的实现(存储),就是我们用到的集合。

2.使用集合的好处

  • 集合类的长度是可变的。
  • 不同的数据存储,操作方式不同。

3. Java的集合体系

Java的集合框架是由很多接口,抽象类,具体类组成的,都位于java.util包中。

在这里插入图片描述

ArrayList:底层是数组实现。

LinkedList:底层是链表实现。

Vector:底层也是数组实现,线程安全。

HashSet:底层是hash表实现。

TreeSet:底层是红黑树实现。

4.集合与数组的区别

都是用来存储数据的容器,两者区别如下:

在这里插入图片描述

5. Collection接口

Collection接口:定义了存取一组对象的方法,其子接口SetList分别定义了存储方式。

  • Set中的数据对象没有顺序且不可以重复。
  • List中的数据对象有顺序且可以重复。

在Collection中定义了一些集合中的共有方法:(代码示例如下)

 public static void main(String[] args) {
        /*
         集合中默认可以存储任意数据类型,建议使用泛型,存储同一种类型的数据
         */
        Collection<String> c = new ArrayList<String>();
        c.add("abd");
        c.add("ab");
        c.add("c");
        c.add("d");

//        c.remove("a");//移除指定元素
//        System.out.println(c.remove("a"));//移除指定元素,集合中包含指定元素返回true
//        System.out.println(c.remove("f"));//移除指定元素,集合中不包含指定元素返回false

        //有条件的删除操作
//          c.removeIf(new Predicate<String>() {
//              @Override
//              public boolean test(String s) {
//                  return s.startsWith("a");//删除以“a”开头的元素(删除的过滤条件)
//              }
//          });

//        c.clear();// 清空集合
//        System.out.println(c.isEmpty());//判断集合是否为空
//        System.out.println(c.contains("a"));//判断集合中是否包含指定元素 ,包含返回true
//        System.out.println(c.contains("f"));//不包含返回false

        //流的方式遍历集合
        c.stream().forEach((a)-> System.out.println(a));
        System.out.println(c);
    }

6. List接口及实现类

List继承了Collection接口,有三个实现的类:

(1). ArrayList

ArrayList底层是数组实现,长度可变,在内存中分配连续的空间。查询快,增删慢,遍历元素和随机访问元素的效率比较高。创建之初,在底层常见一个默认长度的数组,当数组内容添加满了之后,再继续添加时,会扩容一个新数组,为原来的1.5倍。

**缺点:**扩容后,元素不能存满,造成空间浪费。

(方法及代码示例如下)

 public static void main(String[] args) {
//        List<String> list =new ArrayList<>();
        /*
        调用默认无参的构造方法,创建ArrayList对象时,并没有实际的创建数组
          第一次添加元素时创建,默认长度为10
         */
        ArrayList<String> alist = new ArrayList<String>();
        alist.add("a");//给集合中添加元素
        alist.add("b");
        alist.add("c");
        alist.add("d");
        alist.add("e");
        alist.add("e");
        alist.add("e");//当add添加元素时,数组满了后,会扩容,为原来的1.5倍,返回一个新数组
        alist.add(3,"A");//向指定位置添加

//        System.out.println(alist.remove(4));//根据索引删除
//        System.out.println(alist.remove("e"));//根据内容删除,删除指定元素(第一次出现的位置)

//        System.out.println(alist.get(2));//获得指定索引的元素

//        System.out.println(alist.set(2,"C"));//替换指定位置上的元素
//        System.out.println(alist.indexOf("a"));//获取指定元素的下标
//        System.out.println(alist.lastIndexOf("e"));//从后往前,获取指定元素的下标

//        List<String> newlist = alist.subList(2,5);//获取指定范围的元素集合
//        System.out.println(list);

        System.out.println(alist);

        /*
        调用有参的构造方法,创建ArrayList对象时,创建一个指定长度的数组。
         this.elementData = new Object[initialCapacity];
         ArrayList 最大容量  MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
         */
        ArrayList<String> alist1 = new ArrayList<String>(10);
    }
  public static void main(String[] args) {

        ArrayList<String> alist = new ArrayList<String>();
        alist.add("a");
        alist.add("d");
        alist.add("c");
        alist.add("b");
        alist.add("a");

        // 排序
       alist.sort(new Comparator<String>() {
            //使用匿名内部类,定义排序规则
            @Override
            public int compare(String o1, String o2) {

                return o1.compareTo(o2);
            }
        });
        //使用 流 的形式 遍历集合中的元素
        alist.forEach((a)-> System.out.println(a));

        System.out.println(alist);
public static void main(String[] args) {

        ArrayList<String> alist = new ArrayList<String>();
        alist.add("a");
        alist.add("b");
        alist.add("c");
        alist.add("d");

        ArrayList<String> alist1 = new ArrayList<String>();
        alist1.add("a");
        alist1.add("b");
        alist1.add("c");
        alist1.add("e");

//        alist.addAll(alist1);//把指定的集合中的元素添加到此集合中

//        System.out.println(alist.containsAll(alist1));//此集合中是否包含指定集合中的元素,包含返回true
//        System.out.println(alist.contains("h"));//判断此集合中是否包含指定元素  包含返回true

//        System.out.println(alist.removeAll(alist1));//删除两个集合中相同的元素,有返回true

        System.out.println(alist.retainAll(alist1));//保留两个集合中相同的元素,有变化返回true


        System.out.println(alist);

(2). LinkedList

LinkedList:底层是链表实现,采用链表存储方式。查询慢,增删快,插入、删除元素时效率比较高。查找元素时,从第一个节点往后查找,增加、删除元素时其他元素的位置不动,只需要改变指针域的值即可。

(方法及代码示例如下)

    public static void main(String[] args) {

        LinkedList<String> llist = new LinkedList<>();
        //LinkedList里面还提供了许多关于链表头尾操作的方法,这些方法用于模拟队列结构,栈结构操作.
        llist.addLast("a");
        llist.addLast("b");
        llist.addLast("c");
        llist.addLast("d");
        llist.addLast("e");
        System.out.println(llist.size());
//        llist.clear();  //清空集合中的元素

        System.out.println(llist.pollFirst());//检索并删除此列表的第一个元素,如果此列表为空,则返回 null
        System.out.println(llist.peekFirst());//检索但不删除此列表的第一个元素,如果此列表为空,则返回 null
//        System.out.println(llist.getFirst());//返回此列表中的第一个元素。
//        System.out.println(llist.removeFirst());//从此列表中删除并返回第一个元素
        System.out.println(llist);

        llist.addFirst("1");//在该列表开头插入指定的元素 ,无返回值
        llist.offerFirst("1");//在此列表的前面插入指定的元素.返回值是boolean类型
        llist.offerLast("w");//在该列表的末尾插入指定的元素
        System.out.println(llist.offerFirst("1"));
        System.out.println(llist);
    }

(3). Vector

Vector:数组列表,添加同步锁,线程安全的。

   public static void main(String[] args) {

        /*
        Vector 底层也是数组实现
        new Vector<>();  在创建Vector对象时,就已经创建底层存储数组,长度默认为10
        Vector 中的方法都是线程安全
         */
        Vector<String> v = new Vector<>();
        v.add("a");
        v.add("a");
        v.add("b");
        v.add("c");
        v.add("a");
        System.out.println(v); // 输出元素可以重复 [a, a, b, c, a]

    }

7. List接口集合迭代

遍历集合元素的方法:

public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("d");
        list.add("d");
        list.add("d");

        // list接口实现类的遍历 方式1 使用流的形式遍历集合中的元素
//        list.stream().forEach((a)-> System.out.println(a));

        // 遍历方式 2  for循环遍历
     /*   for (int i = 0; i < list.size(); i++) {
//            System.out.println(list.get(i));
            //for循环时,可以从集合中删除元素,但是要注意下标与集合的的内容就会不对应
            if(list.get(i).equals("d")){
                list.remove(i);
                //i--;
            }
        }
        System.out.println(list); */

        //遍历方式3 增强for循环
        for(String t : list){
//            System.out.println(t);
            /*
            理论上,增强for循环中不允许删除元素
            如果必须要删除元素,则只能删除一个元素,并立即break终止循环
             */
            list.remove("d");
            break;
//            if (t.equals("d")){
//                list.remove(t);
//                break;
//            }
        }
        System.out.println(list);
    }

迭代器遍历(Iterator):通过集合返回迭代器

(方法及代码示例如下)

 public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("d");
        list.add("d");
        list.add("d");
        //迭代器 通过iterator() 方法 返回一个 Iterator对象
        // 迭代器
        Iterator<String> it =  list.iterator();
        while(it.hasNext()){//hasNext() 判断集合中还有没有元素
            String s = it.next();
            //System.out.println(s);
            if (s.equals("d")){
                            //remove()  删除元素,删除后 里面的游标(指针、计数器)会往回退一下
                it.remove();// 使用迭代器中提供的方法
            }
        }
        System.out.println(list);
    }
public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        list.add("a");
        list.add("b");
        list.add("c");
        list.add("d");
        list.add("d");
        list.add("d");
        list.add("d");

        //迭代器 通过iterator() 方法 返回一个 Iterator对象


        /*
         迭代器1  从前向后遍历
         ListIterator() 只适合于List接口实现类
            遍历时还可以添加元素,设置替换元素,nextIndex()元素位置
         */
       /* ListIterator<String> listit = list.listIterator();

         while (listit.hasNext()){ //有没有上一个元素
            //System.out.println(listit.next()+":"+listit.nextIndex());  //获取元素及对应位序
             listit.next();
//             listit.add("x"); // 添加元素
//             listit.set("s"); // 修改元素(全部)
//             listit.remove(); //删除元素
        }
        System.out.println(list);*/

        // 迭代器2 从后向前遍历
        ListIterator<String> listit = list.listIterator(list.size());
        System.out.println(listit.hasPrevious());
        while (listit.hasPrevious()){ //有没有上一个元素
            System.out.println(listit.previous());
        }
        System.out.println(list);
    }

8.Set接口及实现类

Set接口继承了Collection接口。

Set中所存储的元素是不可重复的,但是是无序的,Set中的元素没有索引

​ Set接口有两个类:HashSetTreeSet

(1)HashSet

HashSet类中的元素不能重复,即彼此调用equals方法比较,都返回false。

底层数据结构是哈希表+链表。

注:哈希表依赖于哈希值存储

 public static void main(String[] args) {
        /*
        Set  不能存储重复元素
        HashSet  无序存储(不是按照添加顺序排列)
         */
        HashSet<String> h = new HashSet<>();
        h.add("a");
        h.add("a");
        h.add("b");
        h.add("c");
        h.add("x");
        h.add("w");
        h.add("p");
        h.add("通话");
        h.add("重地");
        h.add("H"); // add 了九次,但集合的size为8,因为有一个重复的a没有存进去
        System.out.println(h); //[p, a, b, 通话, 重地, c, w, x, H]
        System.out.println(h.size()); //  b, 通话, 重地 宏观上三者在哈希表的第二位(从0开始) 微观上这三个通过链表联系

        /*  几个问题:
        HashSet是如何判断内容重复
       1. 每次添加内容是,会用内容的hash值来判断是否相同,但是hash值这种方法不安全,
     可能会出现内容不同,hash值相同(块)
       2. 当出现内容不同,hash值相同(hash碰撞,冲突)时,使用equals方法比较,比较
     的是内容是否相同(安全)
         (双保险:保证效率,又保证了安全)
       3.hash值怎么来?
          调用hashCode()方法
        两种情况: 1. 类中已经重写hashCode(),例如String类,根据内容来计算hash值
                2. 类中没有重写hashCode(),调用Object类中hashCode().(可以想成
                对象在内存中的地址)。
         */
    }

(2)TreeSet

TreeSet:可以给集合中的元素进行指定方式的排序(可以按照元素的自然顺序排序)。存储的对象必须实现Comparable接口。

TreeSet底层数据结构是二叉树。(红黑树是一种自平衡的二叉树)

public static void main(String[] args) {
        /*
        Set 不能存储重复元素
        TreeSet 可以按照元素的自然顺序排序
           底层是红黑树
         */
        TreeSet<String> tset = new TreeSet<>();
        tset.add("b");
        tset.add("a");
        tset.add("d");
        tset.add("c");
        tset.add("c");
        System.out.println(tset);//  [a, b, c, d]
    }

(3)Set遍历方式

  public static void main(String[] args) {
        TreeSet<String> tset = new TreeSet<String>();
        tset.add("b");
        tset.add("a");
        tset.add("d");
        tset.add("c");
        tset.add("x");

        // Set 遍历方式1
/*
        tset.stream().forEach((a)-> System.out.println(a));
*/

        // 增强for循环2
/*        for (String t : tset) {
            System.out.println(t);
        }*/

        // 迭代器遍历3
        Iterator<String> it = tset.iterator();
        while (it.hasNext()){
            String e = it.next();
            System.out.println(e);
            
            /*
            a
            b
            c
            d
            x
            */
        }

    }

9.Collections的一些方法

 public static void main(String[] args) {
/*

        ArrayList<String> list = new ArrayList<>();

        Collections.addAll(list,"a","s","m","f"); // 将多个元素插入到指定集合中去
        System.out.println(list); //[a, s, m, f]
        Collections.sort(list); // 排序
        System.out.println(list);//[a, f, m, s]
        System.out.println(Collections.binarySearch(list,"b")); //返回指定元素在集合中的 位序, >0集合中存在该元素,<0 b不存在
*/

        ArrayList<String> list1 = new ArrayList<>();
        list1.add("a");
        list1.add("c");
        list1.add("d");
        list1.add("f");
        ArrayList<String> list2 = new ArrayList<>();
        list2.add("x");
        list2.add("y");
        list2.add("z");
        Collections.copy(list1,list2); //  把list2 中的元素复制到list1中相应的位置上,list1中与list2中对应位置的元素会被覆盖
                                       //  要求目标集合的size不能小于源集合的size
        System.out.println(list1); //[x, y, z, f]
        Collections.fill(list2,"g");// 用指定元素替换掉集合中所有的元素
        System.out.println(list2); //[g, g, g]
        System.out.println(Collections.max(list1)); //z  根据其元素的 自然顺序返回给定集合的最大元素。
        Collections.replaceAll(list1,"f","F"); // 将列表中一个指定值的所有出现替换为另一个。
        System.out.println(list1); //[x, y, z, F]
        Collections.reverse(list1);//  逆序输出
        System.out.println(list1);// [F, z, y, x]
        Collections.swap(list1,0,2);// 交换集合中指定两个位置的元素
        System.out.println(list1);//[y, z, F, x]

    }

10.Map接口及其实现类

Map集合采取键值对存储,键不能重复,值可以重复,键唯一。

Map接口有两个类:HashMApTreeMap

(1)HashMap

HashMap 是键值对存储,键不重复,如果有重复的键,后面重复键的值会把前面重复键的值覆盖掉,允许存储一个键为null的键值对。

HashMap中元素的key值不能重复,即彼此调用equals方法,返回为false。排列顺序是不固定的。

 public static void main(String[] args) {
        /*
        Map集合
          键 值对 存储。
          键不能重复,键唯一,值可以重复。
         HashMap  键是无序的(不是按照添加顺序排列)
          如果有重复的键,后面的值会把前面的覆盖掉
          可以存储一个为 null 的键
         */
        HashMap<String,String> hm = new HashMap<>();
        hm.put("a","a"); // 通过 put() 进行添加
        hm.put("a","b");  //键不能重复 如果有重复的键,后面的值会把前面的覆盖掉
        hm.put("c","C");
        hm.put("d","d");
        hm.put(null,"n");// 可以存储一个为 null 的键
        hm.put(null,"u");
        System.out.println(hm);

//        hm.remove(null);// 移除指定键值的键值对
//        hm.clear(); //清空集合
        System.out.println(hm.containsKey("e")); // 判断是否包含指定 键 的 键值对
        System.out.println(hm.containsValue("d"));// 判断是否包含指定 值 的 键值对
        System.out.println(hm.isEmpty()); // 判断集合是否非空
        System.out.println(hm.size()); //4  返回集合长度
        System.out.println(hm.get("c")); // 获取指定键 的值

        System.out.println(hm);

(2)HashMap的遍历方式

    public static void main(String[] args) {
        HashMap<String,String> hm = new HashMap<>();
        hm.put("a","a");
        hm.put("b","b");
        hm.put("c","c");
        hm.put("p","p");
        System.out.println(hm); //{p=p, a=a, b=b, c=c}

        /*
        Map  集合 遍历
         */

        /*
        ①  forEach()
         */
//        hm.forEach((k,v)-> System.out.println(k+":"+v));

        /*
        ②  .keySet(); 先获取 hm 中所有的键,存储到Set集合中
          再对Set集合进行遍历,通过每次拿到的key去获得对应的值
         */
/*        Set<String> keyset = hm.keySet();
        for (String key : keyset) {
            System.out.println(key + ":" + hm.get(key));
        }*/

        /*
        ③  .entrySet(); 将Map集合中 键值对封装到一个 Entry 对象中
         */
        Set<Map.Entry<String, String>> entryset = hm.entrySet();
        //增强for循环
/*        for (Map.Entry entey : entryset) {
            System.out.println(entey.getKey() + ":" + entey.getValue());
        }*/
        // 迭代器
         Iterator<Map.Entry<String,String>> it =  entryset.iterator();
         while (it.hasNext()){
             Map.Entry<String,String> entry = it.next();
             System.out.println(entry.getKey()+":"+entry.getValue());

         }
         /*
         p:p
         a:a
         b:b
         c:c
          */
    }

(3)TreeMap

TreeMap是键值对存储,键不重复,可以根据键的自然顺序排序。

TreeMap中所有的元素都保持着某种固定的顺序,如果需要得到一个有序的Map就应该使用TreeMap,key值所在类必须实现Comparable接口。

public static void main(String[] args) {
        /*
        TreeMap
           键值对 键不能重复
           可以根据键的自然顺序排序 ,指定的键的类型的类必须实现Comparable接口(排序时使用)
         */
        TreeMap<Integer,String> tm = new TreeMap<>();
        tm.put(1,"a");
        tm.put(1,"b");
        tm.put(3,"d");
        tm.put(2,"c");

//        tm.put(null,"n");  // 不允许存储为null的键 ,会报错
        System.out.println(tm); //{1=b, 2=c, 3=d}

    
    }

(4)HashTable

public static void main(String[] args) {
        /*
        Hashtable
           无序的, 初始容量为11 线程安全
           不允许存储为null的键
         */
        Hashtable<Integer,String> ht = new Hashtable<>();
        ht.put(1,"a");
        ht.put(1,"b");
        ht.put(3,"d");
        ht.put(2,"c");

//        ht.put(null,"u"); // 不允许存储为null的键 ,会报错
        System.out.println(ht);// {3=d, 2=c, 1=b}
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

白居不易.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值