编程语言
首页 > 编程语言> > Java基础自学第六期——集合

Java基础自学第六期——集合

作者:互联网

集合

本文主要介绍一下Java中几个集合的框架。

Collection接口

在Java类库中,集合类的基本接口是Collection接口。这个接口有两个基本方法:

public interface Collection<E>
{
	boolean add(E element);
	Iterator<E> iterator();
}

add方法是向集合中添加元素。如果改变了集合,方法返回值是true,否则返回false。
iterator方法则会返回一个实现Iterator接口的对象。可以使用这个迭代器对象依次访问集合中的元素。

Iterator迭代器

迭代器就是一个对集合进行引用的工具。对于数组我们可以通过下标来对其中元素进行引用。部分集合不支持这种引用,这就需要构造器。
Iterator接口中主要有三个方法:

public interface Iterator<E>
    {
        E next();
        boolean hasNext();
        void remove();
    }

Iterator的简单使用:

ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("11");
Iterator it =  arrayList.iterator();
System.out.println(it.next());
//打印输出11

如果我们需要遍历整个集合,我们则需要hasNext方法来判断时候到达了集合的末尾。当到达了集合的末尾,hasNext会返回false。

ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("11");
arrayList.add("22");
Iterator it =  arrayList.iterator();
while(it.hasNext())
{
    System.out.println(it.next());
}
//打印输出11 22

如果我们只想遍历集合中的元素,我们可以使用for each循环:

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

Iterator接口的remove方法将会删除上次调用next方法时返回的元素。我们可以先next返回该元素,然后remove删除。

集合框架

java集合有两个基本接口:Collection和Map。(图片源于网络)
在这里插入图片描述

Collection类下面分为List,Set和Queue(不做介绍)

List接口

元素存取有序,即先存先取
允许有重复的元素
有索引值,可以使用for循环

List接口的常用方法:

void add(int index,E element)
void remove(int index)
E get(int index)
E set(int index,E element)

也可以使用迭代器来访问。
由数组支持的有序集合可以快速地随机访问,因此适合使用List方法并提供一个整数索引来访问,如ArrayList。而链表支持的有序结合尽管也是有序的,但是通过索引访问很慢,所以最好使用迭代器来遍历,如LinkedList。
ArrayList在前面我们介绍过,所以接下来介绍一下LinkedList。
在数据结构中,我们学过链表。每个节点保存着自己的信息和对下一个节点的引用。在Java中,所有链表实际上都是双向链接的——即每个节点还存放着指向前去节点的引用。

LinkedList方法

public static void main(String[] args){
        LinkedList<String> l = new LinkedList<>();
        l.add("amy");  //向链表末尾添加
        l.add(1,"tom");  //向链表1号索引位置插入
        l.add("john");
        l.get(1);  //返回1号索引位置的元素
        l.getFirst();  //返回链表第一个元素
        l.getLast();  //返回链表最后一个元素
        l.remove();  //移除表头元素
        l.remove("john");   //移除指定的某个元素
        l.remove(1);  //移除1号索引位置的元素
        l.removeFirst();  //移除表头元素
        l.removeLast();  //移除表尾元素
    }

当我们插入删除链表元素时,其他元素的索引位置也会相对改变

Set接口

不允许有重复的元素
没有元素索引,不能for循环
TreeSet和HashSet是存取无序的
LinkedHashSet是存取有序的

有一种众所周知的数据结构们可以快速地查找所需要地对象,这就是散列表(hash table)。散列表为每一个对象计算一个整数,成为散列码(hash code)。散列码是由对象的实例域产生的一个整数。更准确地说,具有不同数据域的对象将产生不同的散列码。
在Java中,散列表采用链表数组实现。每个链表被称为桶。每一个散列码对应一个桶。若出现多个元素对应一个桶,这种现象被称为散列冲突,这时候会将多个元素按照链表给连接起来。

在Java 8中,一个桶超过8个元素会从链表变为平衡二叉树。这样当产生冲突的元素过多时,能提高查找的性能

HashSet

HashSet就是通过hashcode来确定元素的位置,一个hash code上可以存储多个元素。HashSet通过hashcode和equals方法可以快速查看元素是否在集合中重复。
当你试图把对象加入HashSet时,HashSet会使用对象的hashcode来判断对象加入的位置。同时也会与其他已经加入的对象的hashcode进行比较,如果没有相等的hashcode,HashSet就会假设对象没有重复出现。

如果对象的hashCode值是不同的,那么HashSet会认为对象是不可能相等的。

因此我们自定义类的时候需要重写hashCode,来确保对象具有相同的hashCode值。
如果元素(对象)的hashCode值相同,是不是就无法存入HashSet中了? 当然不是,会继续使用equals 进行比较.如果 equals为true 那么HashSet认为新加入的对象重复了,所以加入失败。如果equals 为false那么HashSet 认为新加入的对象没有重复.新元素可以存入。

public static void main(String[] args){
        HashSet<String> set = new HashSet<>();
        set.add("amy");
        set.add("tom");
        set.add("amy");
        Iterator<String> it = set.iterator();
        while(it.hasNext())
        {
            System.out.println(it.next());
        }

    }
    //打印tom amy

从打印结果我们可以发现,HashSet是存取无序的,而且会默认地去掉重复地元素。如果我们现在需要按照一定的顺序存取元素,并且要求去重,这时候就需要TreeSet了。

TreeSet

TreeSet与HashSet很类似,不过它是一个有序集合。在对集合进行遍历时,每个值将自动地按照排序后地顺序实现。TreeSet底层是通过红黑树实现的。
TreeSet自定义排序可以通过两个方式来实现:

要使用TreeSet,必须能够比较元素。这些元素必须实现Comparable接口,或者构造集时必须提供一个Comparator

LinkedHashSet

LinkedHashSet底层则是链表+散列表实现的,存取有序。可以看作散列表按照链表插入顺序来存取。

Map

在Set接口中,我们通常使用hashcode作为元素的索引值。但有时候我们已经有要查找的元素的索引了。这时候我们可以用映射来存放键值对。如果我们知道了键,就可以查找到相对应的值。Java中提供了HashMap和TreeMap两种框架。
HashMap对键进行散列,TreeMap则用键的排序顺序来对值进行排序,并将其组织成搜索树。散列或比较函数只能作用于键,与键关联的值不能进行散列或者比较。
HashMap和TreeMap的特点与HashSet和TreeSet一样。HashMap查找快,TreeMap则会按照排列顺序访问键。

基本映射操作

public class Launch {
    public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "amy");
        map.put(2, "tom");
        map.put(3, "john");
        System.out.println(map.get(1));
    }
}

在创建map的时候,我们需要定义映射中键值对的数据类型。put方法将键值对插入到映射中。如果这个键已经存在,那么新的值则会取代旧的值。
get方法则会返回与键对应的值。如果映射中没有这个键则会返回null。

map的遍历

对于map的遍历,我们首先要获得map的键集合,通过不断获得键,来找到值。

public static void main(String[] args) {
        HashMap<Integer, String> map = new HashMap<Integer, String>();
        map.put(1, "amy");
        map.put(2, "tom");
        map.put(3, "john");
        Set<Integer> set = map.keySet();
        Iterator<Integer> it = set.iterator();
        while(it.hasNext())
        {
            int i = it.next();
            System.out.println(i+" "+map.get(i));
        }

    }

通过keySet方法返回一个存储键的集合,然后遍历集合即可找到值。也可以使用for each循环

	for(int i : set)
        {
            System.out.println(i+" "+map.get(i));
        }

Map类中提供了一个同时反映键与值的方法entrySet():

Set<Map.Entry<Integer, String>> entries = map.entrySet();

该方法会返回一个保存着键值对的一个集合。Map.Entry接口提供了getKey和getValue方法:

for(Map.Entry<Integer, String> entry: map.entrySet())
        {
            System.out.println(entry.getKey()+" "+entry.getValue());
        }

往期回顾

第一期——继承
第二期——反射与数组
第三期——接口
第四期——内部类与lambda
第五期——异常

标签:map,Java,HashSet,元素,接口,链表,集合,自学,第六期
来源: https://blog.csdn.net/weixin_48469642/article/details/113800497