9. 集合

1.集合概述

Java 集合就像一种容器,可以把多个对象的引用放入容器中。
Java 集合类可以用于存储数量不等的多个对象,还可用于保存具有映射关系的关联数组
Java 集合可分为 Set、List 和 Map 三种体系

  • Set:无序、不可重复的集合
  • List:有序,可重复的集合
  • Map:具有映射关系的集合
    在 Java5 之前,Java 集合会丢失容器中所有对象的数据类型,把所有对象都当成 Object 类型处理;从 Java5 增加了泛型以后,Java 集合可以记住容器中对象的数据类型。
    在这里插入图片描述

2.Collection接口

Collection 接口是 List、Set 和 Queue 接口的父接口,该接口里定义的方法既可用于操作 Set 集合,也可用于操作 List 和 Queue 集合:
在这里插入图片描述

3.List接口

1).List接口简介

List接口为Collection直接接口。List所代表的是有序的Collection,即它用某种特定的插入顺序来维护元素顺序。用户可以对列表中每个元素的插入位置进行精确地控制,
同时可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。实现List接口的集合主要有:ArrayList、LinkedList、Vector、Stack。

2).ArrayList集合

ArrayList内部是通过数组实现的,它允许对元素进行快速随机访问。数组的缺点是每个元素之间不能有间隔,当数组大小不满足时需要增加存储能力,就要讲已经有数组的数据复制到新的存储空间中。
当从ArrayList的中间位置插入或者删除元素时,需要对数组进行复制、移动、代价比较高(ArrayList在内存不够时默认是扩展50% + 1个)。因此,它适合随机查找和遍历,不适合插入和删除。

public class Test3 {
    public static void main(String[] args) {
        List<String > list=new ArrayList<>();
        list.add("456");//追加到末尾
        list.add("hello");
        list.add("12311");
        list.add("12322");
        //1.for
        for(int i=0;i<list.size();i++){
            System.out.println(list.get(i));
        }
        //2.foreach
        for(String s:list){
            System.out.println(s);
        }
        //3.迭代器
        System.out.println("=============");
        Iterator<String> it = list.iterator();
        /*while (true){
            boolean b = it.hasNext();
            if(!b){
                break;
            }
            System.out.println(it.next());
        }*/
        while (it.hasNext()){
            System.out.println(it.next());
        }
    }
}
3).LinkedList集合

LinkedList是用双向链表结构存储数据的,很适合数据的动态插入和删除,随机访问和遍历速度比较慢。另外,他还提供了List接口中没有定义的方法,专门用于操作表头和表尾元素,可以当作堆栈、队列和双向队列使用。

4).Vector集合
            Vector集合底层数据结构是数组实现的,其操作和ArrayList一致,查询快,增删慢,但是其内部是线程安全,效率低。                         
5).Itrator接口

Iterator 接口主要用于遍历 Collection 集合中的元素,Iterator 对象也被称为迭代器
Iterator 接口隐藏了各种 Collection 实现类的底层细节,向应用程序提供了遍历 Collection 集合元素的统一编程接口
Iterator 仅用于遍历集合,Iterator 本身并不提供承装对象的能力。如果需要创建 Iterator 对象,则必须有一个被迭代的集合。
在这里插入图片描述

6).ArrayList和LinkedList的区别如下:

ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
案例1:写一个集合类
需求:编写一个ListCopy类,类具有和ArrayList相似的功能

public class ListCopy {
    private int length;
    private Object[] obj;
    private int index = 0;

    public ListCopy(int length) {
        this.length = length;
        obj = new Object[length];
    }

    public void add(Object o) {
        //如何判断数组是否达到上限
        if (index == length) {
            //如果上限了,就要扩容
            //如何扩容?新建一个数组,长度是原来的1.5倍,把旧数组内容复制到新数组
            Object[] newobj = new Object[length + length / 2];
            System.arraycopy(obj, 0, newobj, 0, length);
            //旧:hello hello1 hello2
            //新:hello hello1 hello2 null
            newobj[length] = o;
            obj = newobj;
            index++;
            length = length + length / 2;
        } else {
            obj[index] = o;
            index++;
        }

    }

    public void show() {
        for (Object o : obj) {
            System.out.println(o);
        }
    }

    public int size() {
        return index;
    }

    public Object get(int i) {
        return obj[i];
    }


    public boolean contain(Object hello) {
        for (Object o : obj) {
            if(o!=null){
                if (o.equals(hello)) {
                    return true;
                }
            }
        }
        return false;
    }

    public boolean remove(int i) {
        if(i>index){
            return false;
        }else{
            Object[] newobj=new Object[length];
            System.arraycopy(obj,0,newobj,0,i);
            System.arraycopy(obj,i+1,newobj,i,length-i-1);
            obj=newobj;
            index--;
            return true;
        }
    }
}

测试ListCopy

public class Main {
    public static void main(String[] args) {
        ListCopy listCopy = new ListCopy(3);
        listCopy.add("hello");
        listCopy.add("hello1");
        listCopy.add("hello2");
        listCopy.add("hello4");
        boolean f=listCopy.remove(1);
        listCopy.show();
        boolean flag = listCopy.contain("hello1");
        System.out.println(flag);
        int size = listCopy.size();
        String s = (String) listCopy.get(3);
        System.out.println(s);
        System.out.println(size);
    }
}

案例2:模拟KTV点歌系统

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class Test {
    private static List<String> list = new ArrayList<>();

    public static void main(String[] args) {
        //模拟KTV点歌系统
        //歌曲列表  置顶  移到上一首  移除歌曲 添加
        initsong();
        mainScreen();
    }

    private static void mainScreen() {
        System.out.println("=======欢迎来到KTV========");
        System.out.println("1.查看列表");
        System.out.println("2.置顶歌曲");
        System.out.println("3.移到上一首");
        System.out.println("4.移除歌曲");
        System.out.println("5.添加歌曲");
        System.out.println("0.退出系统");
        Scanner scanner = new Scanner(System.in);
        System.out.println("输入您的操作:");
        int i = scanner.nextInt();
        switch (i) {
            case 0:
                loginout();
                break;
            case 1:
                show();
                break;
            case 2:
                totop();
                break;
            case 3:
                up();
                break;
            case 4:
                delete();
                break;
            case 5:
                addsong();
                break;
        }
    }

    private static void initsong() {
        list.add("两只老虎");
        list.add("光辉岁月");
        list.add("长城");
        list.add("心太软");
        mainScreen();
    }

    private static void delete() {
        System.out.println("输入要删除的歌曲:");
        Scanner scanner = new Scanner(System.in);
        String song = scanner.nextLine();
        list.remove(song);
        mainScreen();
    }

    private static void up() {
        System.out.println("输入要移动的歌曲:");
        Scanner scanner = new Scanner(System.in);
        String song = scanner.nextLine();
        int i = list.indexOf(song.trim());
        if (i < 0) {
            System.out.println("所移动歌曲不存在");
        } else if (i == 0) {
            System.out.println("歌曲已经在顶部!");
        } else {
            list.remove(song.trim());
            list.add(i - 1, song);
            show();
        }
        mainScreen();
    }

    private static void totop() {
        System.out.println("输入置顶歌曲:");
        Scanner scanner = new Scanner(System.in);
        String song = scanner.nextLine();
        list.remove(song.trim());
        list.add(0, song);
        show();
        mainScreen();
    }

    private static void show() {
        System.out.println(list);
        mainScreen();
    }

    private static void addsong() {
        System.out.println("输入添加歌曲:");
        Scanner scanner = new Scanner(System.in);
        String song = scanner.nextLine();
        list.add(song.trim());
        mainScreen();
    }

    private static void loginout() {
        System.out.println("系统退出");
        System.exit(0);
    }
}

4.Set接口

1).Set接口介绍

Set是Collection子接口;
Set和Collection基本上一样,一点除外:Set无法记住添加的顺序,不允许包含重复的元素。
当试图添加两个相同元素进Set集合,添加操作失败,add()方法返回false。
Setr如何判断两个对象是否相等?HashSet 集合的add()方法底层依赖于双列集合HashMap, 它依赖于两个方法 equals()和hashCode(); 先比较元素equals(), 再比较hashCoede值.
也就是说两个对象equals比较返回true,Set集合是不会接受这个两个对象的。
常用子类:
HashSet:散列存放
TreeSet:有序存放

2).HashSet集合

HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。
HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取和查找性能。
HashSet 具有以下特点:

  • 不能保证元素的排列顺序
  • HashSet 不是线程安全的
  • 集合元素可以是 null
    当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法来得到该对象的 hashCode 值,然后根据 hashCode 值决定该对象在 HashSet 中的存储位置。如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。
3).TreeSet集合

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。
TreeSet 支持两种排序方法:自然排序和定制排序。默认情况下,TreeSet 采用自然排序。
自然排序
TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序排列
如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable 接口。
实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过 compareTo(Object obj) 方法的返回值来比较大小。
Comparable 的典型实现:
BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较
Character:按字符的 UNICODE 值来进行比较
Boolean:true 对应的包装类实例大于 false 对应的包装类实例
String:按字符串中字符的 UNICODE 值进行比较
Date、Time:后边的时间、日期比前面的时间、日期大
自然排序步骤:
1.让元素自身具备比较性,
2.实现Compareable接口,覆盖其CompareTo方法

public int compareTo(Object obj) 
    {
       if(!(obj instanceof Student)) {
           throw new RuntimeException("不是学生对象");}
       Student s = (Student)obj;
       System.out.println(this.name+"....compareto....."+s.name);
       if(this.age>s.age) 
           return 1;
       if(this.age==s.age)
       {
           return this.name.compareTo(s.name);
       }
       return -1;
    }

定制排序
如果需要实现定制排序,则需要在创建 TreeSet 集合对象时,提供一个 Comparator 接口的实现类对象。由该 Comparator 对象负责集合元素的排序逻辑
定制排序步骤:
1)创建比较器,实现comparator接口
2)复写compare方法
3)在创建TreeSet集合对象时,提供一个一个Comparator对象

class MyComparator implements Comparator{//第一步:实现Comparator接口
public int compare(Object o1, Object o2) {//第二步:实现一个campare方法
 if(o1 instanceof Student1 & o2instanceof Student1){
           Student1 s1 =(Student1)o1;
           Student1 s2 =(Student1)o2;
           if(s1.getAge() > s2.getAge()){
              return -1;
           }else if(s1.getAge() < s2.getAge()){
              return 1;
           } }
       return 0;
}} 

Set s= new TreeSet(new MyComparator());//第三步:创建TreeSet集合对象时,提供一个一个Comparator对象

5.Map接口

1).Map接口介绍

Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 Key,另外一组用于保存 Map 里的 Value
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 Key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较中返回 false
Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到唯一的,确定的 Value。
在这里插入图片描述

2).HashMap集合

HashMap是 Map 接口使用频率最高的实现类。
HashMap 是基于哈希表的 Map 接口的非同步实现。此实现提供所有可选的映射操作,并允许使用 null 值和 null 键。与HashSet一样,此类不保证映射的顺序,特别是它不保证该顺序恒久不变。
HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。
HashMap 判断两个 value相等的标准是:两个 value 通过 equals() 方法返回 true。

3).遍历HashMap

①Map集合遍历键找值方式:即通过元素中的键,获取键所对应的值
操作步骤:
1.获取Map集合中所有的键,由于键是唯一的,所以返回一个Set集合存储所有的键。
2.遍历键的Set集合,得到每一个键
3.根据键,获取键所对应的值

public class Main {
    public static void main(String[] args) {
        Map<String,String> map=new HashMap<>();
        map.put("007","hello");
        map.put("002","hello2");
        map.put("003","hello3");
        map.put("004","hello4");
        map.put("005","hello5");
       Set<String> keys = map.keySet();
        Iterator<String> iterator = keys.iterator();
        while (iterator.hasNext()){
            String key=iterator.next();
            System.out.println(key+":"+map.get(key));
        }
	}
}

②Map集合遍历键值对方式:即通过集合中每个键值对(Entry)对象,获取键值对对象中的键与值
操作步骤:
1.获取Map集合中,所有的键值对(Entry)对象,以Set集合形式返回。
2.遍历包含键值对(Entry)对象的Set集合,得到每一个键值对(Entry)对象
3.通过键值对(Entry)对象,获取Entry对象中的键与值。

public class Main {
    public static void main(String[] args) {
        Map<String,String> map=new HashMap<>();
        map.put("007","hello");
        map.put("002","hello2");
        map.put("003","hello3");
        map.put("004","hello4");
        map.put("005","hello5");
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Iterator<Map.Entry<String, String>> iterator = entries.iterator();
        while(iterator.hasNext()){
            Map.Entry<String, String> entry=iterator.next();
            System.out.println(entry.getKey()+" :" +entry.getValue());
        }
    }
}
3).TreeMap集合

TreeMap是用键来进行升序顺序来排序的。通过Comparable 或 Comparator来排序。

package pm;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

public class Test1 {
    public static void main(String[] args) {
        //输入一串字符串,然后输出每个字符出现的次数,用TreeMap
        String s="aklsdhajhfakjhgdasdgahg中华787人民共45678和国";
        Map<Character,Integer> map=new TreeMap<>();
        char[] chars = s.toCharArray();
        for(int i=0;i<chars.length;i++){
            if(map.get(chars[i])==null){
                map.put(chars[i],1);
            }else{
                map.put(chars[i],map.get(chars[i])+1);
            }
        }
        Set<Map.Entry<Character, Integer>> entries = map.entrySet();
        Iterator<Map.Entry<Character, Integer>> iterator = entries.iterator();
        while (iterator.hasNext()){
            Map.Entry<Character,Integer> entry=iterator.next();
            System.out.println(entry.getKey()+":"+entry.getValue());
        }
    }
}
4).Properties集合

Properties 类是 Hashtable 的子类,该对象用于处理属性文件
由于属性文件里的 key、value 都是字符串类型,所以 properties 里的 Key 和 Value 都是字符串类型的
案例7-3:斗地主小游戏之洗牌发牌

package pm;

import java.util.*;

public class Tset3 {
    public static void main(String[] args) {
        //斗地主发牌
        //先把牌弄出来,2个集合,一个集合装花色,一个集合装数字
        List<String> type=new ArrayList<>();
        type.add("♥");
        type.add("♠");
        type.add("♦");
        type.add("♣");
        List<String> typecount=new ArrayList<>();
        for(int i=3;i<=10;i++){
            typecount.add(""+i);
        }
        //把非数字牌添加进来
        typecount.add("J");
        typecount.add("Q");
        typecount.add("K");
        typecount.add("A");
        typecount.add("2");
        //创建牌的map,每张牌都有一个编号,编号从0-53,把每张牌装进去
        Map<Integer,String> poke=new HashMap<>();
        int index=0;
        for(String s:typecount){
            for(String sc:type){
                poke.put(index,s+sc);
                index++;
            }
        }
        poke.put(52,"大王");
        poke.put(53,"小王");
        System.out.println(poke);
        //创建一个牌编号集合,以便洗牌
        List<Integer> number=new ArrayList<>();
        for(int i=0;i<54;i++){
            number.add(i);
        }
        //洗牌,编号乱了
        Collections.shuffle(number);
       //需要4个集合来装牌对应的编号,3个玩家,1个底牌
        List<Integer> p1=new ArrayList<>();
        List<Integer> p2=new ArrayList<>();
        List<Integer> p3=new ArrayList<>();
        List<Integer> buttonpoke=new ArrayList<>();
        //发牌对应的编号
        for(int i=0;i<54;i++){
            if(i>=51){
                buttonpoke.add(number.get(i));
            }else{
                if(i%3==1){
                    p1.add(number.get(i));
                }else  if(i%3==2){
                    p2.add(number.get(i));
                }else  if(i%3==0){
                    p3.add(number.get(i));
                }
            }
        }
        //对编号排序
        Collections.sort(p1);
        Collections.sort(p2);
        Collections.sort(p3);
        Collections.sort(buttonpoke);
        //需要4个集合来装牌
        List<String> p11=new ArrayList<>();
        List<String> p22=new ArrayList<>();
        List<String> p33=new ArrayList<>();
        List<String> buttonpoke1=new ArrayList<>();
        //根据编号去牌库获取对应的牌
        for(Integer i:p1){
           p11.add(poke.get(i));
        }
        for(Integer i:p2){
            p22.add(poke.get(i));
        }
        for(Integer i:p3){
            p33.add(poke.get(i));
        }
        for(Integer i:buttonpoke){
            buttonpoke1.add(poke.get(i));
        }
        System.out.println("玩家1:"+p11);
        System.out.println("玩家2:"+p22);
        System.out.println("玩家3:"+p33);
        System.out.println("底牌:"+buttonpoke1);
    }
}

6.总结

1)Set集合和List集合的区别?
Set: 不允许元素重复, 集合元素唯一(元素可以为null), 不能保证迭代顺序恒久不变, 无序(存储和取出不一致).
List: 允许元素重复, 并且元素有序(存储和取出一致).
2).Set 集合存储元素时可以保证元素的唯一性, 原因什么?
HashSet 集合的add()方法底层依赖于双列集合HashMap, 它依赖于两个方法 equals()和hashCode(); 先比较元素hashCoede值, 再比较equals().
3).HashSet元素如何添加?
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,判断已经存储在集合中的对象的hashCode值是否与添加的对象的hashCode值一致:若不一致:直接添加进去;若一致,再进行equals方法比较,equals方法如果返回true,表明对象已经添加进去了,就不会再添加新的对象了,否则添加进去;如果我们重写了equals方法,也要重写hashCode方法,反之亦然;。
HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode方法返回值也相等。
4).ArrayList和LinkedList有什么区别?
5).HashMap和HashTable有什么区别?

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

我超爱写bug

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

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

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

打赏作者

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

抵扣说明:

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

余额充值