万字长文带你回顾Java集合框架

目录

1 Collection

1.1 List

1.1.1 AarrayList

1.1.2 Vector

1.1.3 LinkedList

1.2 Set

1.2.1 HashSet

1.2.2 TreeSet

2 Map

2.1 HashMap

2.2 Hashtable

2.3 TreeMap

3 Iterator

4 其他集合遍历输出的方法

4.1 for循环遍历

4.2 foreach循环遍历

4.3 Enumeration


Java集合框架中有三大顶层接口:存储单值的最大父接口Collection,存储双值的最大父接口Map,迭代器Iterator,所有类集都定义在java.util包中,可以直接导包:import java.util.*

1 Collection

Collection接口的定义为:public interface Collection<E> extends Iterable<E>,可见它是可迭代的。接口中定义了 add() addAll() clear() contains() containsAll() isEmpty() iterator() remove() removeAll() retianAll() size() toArray() toArray(T[] a) equals() hashCode() 等15种方法。

JDK1.2之后,一般不直接使用Collection,而是使用其操作的子接口:ListSet,子接口下面有实现类。层级关系如下图所示。

1.1 List

List接口的定义为:public interface List<E> extends Collection<E>,元素允许重复。常用的实现类包括ArrayListVectorLinkedList

List接口扩充的方法有:add() addAll() get() indexOf() lastIndexOf() listIterator() remove() set() subList(),其中涉及元素增删改查的方法均用索引定位元素,第一个参数都是索引。

1.1.1 AarrayList

ArrayList的定义是:

public class ArrayList<E> extends AbstractList<E> 
             implements List<E>, RandomAccess, Cloneable, Serializable

AbstractList为List的抽象子类。ArrayList支持快速随机访问,可以被克隆,可以被序列化。只要内存够大,可以无限往里面添加元素。

代码示例:

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

public class ArrayListDemo {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
        // 增
        myList.add("Sun"); // 这是Collection中的add()
        myList.add(0, "Moon"); // 这是List中的add()
        myList.add("Earth");
        myList.add(myList.size(), "Mars"); // size()是Collection的方法,获取ArrayList的大小
        System.out.println(myList); // 输出: [Moon, Sun, Earth, Mars]
        // 打印时自动调用toString()方法,所以能显示出元素的具体值
        
        // 删
        myList.remove(1); // 根据索引删除元素
        myList.remove("Moon"); // 根据元素值删除元素
        System.out.println(myList); // 输出:[Earth, Mars]
        
        // 查
        System.out.println(myList.get(1)); // List的方法,根据索引获得元素值
        
        // 改
        myList.set(0, "Mercury"); // 修改指定索引中的元素值
        System.out.println(myList);
    }
}

1.1.2 Vector

Vector的定义是:

public class Vector<E> extends AbstractList<E> 
       implements List<E>, RandomAccess, Cloneable, Serializable

Vector的定义和操作方法和ArrayList没有区别,毕竟都是List接口的实现类,操作以共同的接口为准。

Vector早在JDK1.0就已推出,是Java最早的操作动态数组的类。JDK1.2推出了java类的集合框架后,仍然保留了这个类,并进行了升级,让其多实现了一个List接口。相比于ArrayList,除了支持Iterator、ListIterator输出,还支持Enumeration输出,代码案例见 3.3 。另外,Vector采用同步处理,性能低于采用异步处理的ArrayList。

代码示例:

import java.util.Vector;

public class VectorDemo1 {
    public static void main(String[] args) {
        Vector<String> myVector = new Vector<>();
        // 增
        myVector.add("Sun");
        myVector.add(0,"Moon");
        myVector.add("Earth");
        myVector.add(myVector.size(), "Mars");
        System.out.println(myVector); // 输出:[Moon, Sun, Earth, Mars]

        // 删
        myVector.remove("Sun");
        myVector.remove(0);
        System.out.println(myVector); // 输出:[Earth, Mars]

        // 查
        System.out.println(myVector.get(1)); // 输出:Mars

        // 改
        myVector.set(0, "Mercury");
        System.out.println(myVector); // 输出:[Mercury, Mars]

1.1.3 LinkedList

LinkedList的定义是:

public class LinkedList<E> extends AbstractSequentialList<E> 
       implements List<E>, Queue<E>, Cloneable, Serializable

该类为链表类,使用频率非常,既是List接口的子类,也是Queue接口的子类。

Queue中定义的方法包括:add() element() offer() peak() poll() remove()

代码示例:

import java.util.LinkedList;
import java.util.List;
import java.util.Queue;

public class LinkedListDemo {
    public static void main(String[] args) {
        Queue<String> myLink = new LinkedList<>();
        // 增
        myLink.add("Sun"); // add():如果有容量限制且已满,会抛出异常
        myLink.add("Moon");
        myLink.offer("Earth"); // offer():如果有容量限制且已满,会返回false
        myLink.offer("Mars");
        System.out.println(myLink); // 输出:[Sun, Moon, Earth, Mars]

        // 删
        myLink.remove("Sun"); // 仅删除元素
        System.out.println(myLink); // 输出:[Moon, Earth, Mars]
        String ele = myLink.poll(); // poll():删除并取出头元素
        System.out.println(ele); // 输出:Moon
        System.out.println(myLink); // 输出:Earth

        // 查
        String ele1 = myLink.peek(); // peak():取出头元素,不删除,队列为空抛出异常
        System.out.println(ele1); // 输出:Earth
        String ele2 = myLink.element(); // element():取出头元素,不删除,队列为空返回null
        System.out.println(ele2); // 输出:Earth
    }
}

 

1.2 Set

Set接口的定义为:public interface Set<E> extends Collection<E>。和List相比,最大的区别在于Set中的元素不允许重复。另外,Set也没有扩充新的方法,只能调用继承自Collection的方法,没有get(int index)方法,也不能循环输出。

Set有两个常用子类:HashSetTreeSet。对于内部的元素,前者为散列存放,后者为排序存放。

1.2.1 HashSet

HashSet的定义为:

public class HashSet<E> extends AbstractSet<E>
    implements Set<E>, Cloneable, Serializable

AbstractSet是Set的抽象类。HashSet的元素为散列存放,元素没有索引,内部是无序的。

虽然Set本身不能被直接遍历,但可以利用Collection接口中定义的toArray()方法,转换为数组。

代码示例:

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> mySet = new HashSet<>();
        mySet.add("Sun");
        mySet.add("Moon");
        mySet.add("Earth");
        mySet.add("Sun"); // 不能被添加,因为值为Sun的元素已经存在,元素值不可重复
        System.out.println(mySet);
        // 输出:[Earth, Moon, Sun],与添加时顺序并不一致
        
        // 转换为数组后循环遍历
        String[] myArray2 = mySet.toArray(String[]::new);
        // 这种写法也对: mySet.toArray(new String[] {})
        for(int i=0;i<mySet.size();i++) {
            System.out.println(myArray2[i]);
        }
    }
}

对于自定义的类,如果不定义hashCode()equals()方法,实例化的对象进入HashSet中不能实现去重。去重的前提是要判断两个元素是否相等。hashCode()equals()分别提供了一种判断方式,hashCode()判断两个对象的编码是否相等,equals()判断对象中的每个属性是否相等,如果两种方法判断出来的结果都相等,那么认为是重复元素。

代码示例:

Human2类(还有写了一个Human类,为了对比,没有定义hashCode()equals()方法,为节省篇幅,不在此列出)

import java.util.Objects;

public class Human2 {
    private String name;
    private int age;

    public Human2(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "name='" + name + ' ' + "age=" + age;
    }

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

    @Override
    public int hashCode() {
        return Objects.hash(name, age);
    }
}

HashSetDemo2.java:在HashSet中验证去重效果

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo2 {
    public static void main(String[] args) {
        Set<Human> mySet = new HashSet<>();
        mySet.add(new Human("Anna", 20));
        mySet.add(new Human("Betty", 22));
        mySet.add(new Human("Cindy", 24));
        mySet.add(new Human("Cindy", 24));
        System.out.println(mySet); // 重复元素仍在Set里面,并没有实现去重
        /**
         * 输出:[name='Anna age=20, name='Cindy age=24, name='Cindy age=24, 
         name='Betty age=22]
         */

        Set<Human2> mySet2 = new HashSet<>();
        mySet2.add(new Human2("Anna", 20));
        mySet2.add(new Human2("Betty", 22));
        mySet2.add(new Human2("Cindy", 24));
        mySet2.add(new Human2("Cindy", 24));
        System.out.println(mySet2); // 在类中定义了hashCode()和equals()方法后,实现了去重
        // 输出:[name='Anna age=20, name='Cindy age=24, name='Betty age=22]
    }
}

1.2.2 TreeSet

TreeSet的定义为:

public class TreeSet<E> extends AbstractSet<E>
       implements NavigableSet<E>, Cloneable, Serializable

再看NavigableSet的定义:

public interface NavigableSet<E> extends SortedSet<E>

再看SortedSet的定义:

public interface SortedSet<E> extends Set<E>

由此可以清晰地看出这样一个继承/实现关系:

需要注意的是,TreeSet添加元素的过程仍然是无序的,实现排序的前提是使用者定义好排序规则,这就需要在自定义的类中实现Comparable接口,并重写compareTo()方法。  

重写compareTo()方法时,不能以有可能出现相同值的属性作为唯一的排序标准,否则一旦出现相同值,两个对象就会被认为是同一个,由于Set元素的不可重复性,其中一个会被丢弃。

代码示例1:自定义类中定制排序规则

import java.util.Objects;

public class Person implements Comparable<Person> {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    @Override
    public int compareTo(Person o) {
        // 在这个方法里定制排序规则,这里实现了按年龄升序排序,年龄一致就按姓名排序
        if(this.age > o.age){
            return 1;
        }else if(this.age < o.age){
            return -1;
        }else{
            // 如果return 0,即将年龄作为唯一的排序标准,一旦年龄相同,就会被识别为重复元素
            return this.name.compareTo(o.name);
        }
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

代码示例2:TreeSet实现排序

import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

public class TreeSetDemo2 {
    public static void main(String[] args) {
        Set<Person> personSet = new TreeSet<>();
        personSet.add(new Person("Peter",30));
        personSet.add(new Person("Adam",32));
        personSet.add(new Person("Spencer",35));
        System.out.println(personSet); 

输出结果为:[Person{name='Peter', age=30}, Person{name='Adam', age=32}, Person{name='Spencer', age=35}]

2 Map

Map接口的定义为:public interface Map<K,V>。Map的含义是映射,内部存储具有映射关系的键值对。

键值对又被称为二元偶对象,其中键(Key)不允许重复,值(Value)允许重复。

Map中定义的常用方法包括clear() containsKey() containsValue() entrySet() get() isEmpty() keySet() values() put() putAll() putAll() remove()

取出(get)和删除(remove)元素的方法的参数都是,即根据键获取值或删除键值对。由于键的不可重复性,键和键值对只能转化为Set对象;由于值允许重复,因此能够转化为Collection对象。由Map转化过来的Collection对象与Map对象共享一块内存,改变Collection,Map也会产生相应的改变。

Map接口包括三个子类:HashMapHashtableTreeMap.

2.1 HashMap

HashMap的定义为:

public class HashMap<K,V> extends AbstractMap<K,V> 
       implements Map<K,V>, Cloneable, Serializable

 该子类继承了Map的抽象子类AbstractMap,可以被克隆和被序列化。只要内存够大,可以无限往里面添加元素。

HashMap中的元素是无序的。

代码示例:

import java.util.*;

public class HashMapDemo {
    public static void main(String[] args) {
        Map<Integer, String> myMap = new HashMap<>();
        
        // 增
        myMap.put(1, "Anna");
        myMap.put(2, "Bob");
        myMap.put(3, "Cindy");
        myMap.put(4, "David");
        myMap.put(5, "Ellen");
        System.out.println(myMap); // 输出:{1=Anna, 2=Bob, 3=Cindy, 4=David, 5=Ellen}
        
        // 删
        myMap.remove(4); // 根据 键 删除键值对
        System.out.println(myMap); // 输出:{1=Anna, 2=Bob, 3=Cindy, 5=Ellen}
        // 想要按 值 删除,需将所有的值先转换成Collection,再操作Collection
        Collection<String> collection = myMap.values();
        collection.remove("Anna"); // 按 值 删除
        System.out.println(myMap); // 输出:[Bob, Cindy, Ellen]
        
        // 查
        String value = myMap.get(3); // 根据 键 查找值
        System.out.println(value); // 输出:Cindy
        // 将所有的 键 转换为Set 并打印
        Set<Integer> keys = myMap.keySet();
        System.out.println(keys); // 输出:[2, 3, 5]
        // 将所有的 值 转换为Collection 并打印
        Collection<String> values = myMap.values();
        System.out.println(values); // 输出:[Bob, Cindy, Ellen]
        // 获取 Map 的大小
        System.out.println(myMap.size()); // 输出:4
        
        // 改
        myMap.put(2,"Bobby"); // put()方法中传入已有的 键 即可直接实现 值 的修改
        System.out.println(myMap); // 输出:{2=Bobby, 3=Cindy, 5=Ellen}
}

 

2.2 Hashtable

Hashtable的定义为:

public class Hashtable<K,V> extends Dictionary<K,V>
       implements Map<K,V>, Cloneable, Serializable

Hashtable继承了Dictionary类,即字典,定义如下:

class Dictionary<K,V>

Hashtable是最早的键值对操作类,JDK1.0就已推出,基本操作和HashMap类似,内部元素是无序的。相比于JDK1.2才出现的新操作类HashMap,Hashtable采用同步处理方式,性能较低,而且不允许null值。

代码示例:

import java.util.Hashtable;
import java.util.Map;

public class HashtableDemo {
    public static void main(String[] args) {
        Map<Integer, String> myMap = new Hashtable<>();
        myMap.put(1, "Anna");
        myMap.put(2, "Bob");
        myMap.put(3, "Cindy");
        myMap.put(4, "David");
        myMap.put(5, "Ellen");
        System.out.println(myMap); // 输出:{5=Ellen, 4=David, 3=Cindy, 2=Bob, 1=Anna}
        // 增删查操作与HashMap一致,不再演示

2.3 TreeMap

TreeMap的定义为:

public class TreeMap<K,V> extends AbstractMap<K,V>
       implements NavigableMap<K,V>, Cloneable, Serializable

TreeMap继承自Map的抽象子类AbstractMap,实现了NvigableMap接口,用于排序,可以被克隆和序列化。

再看NvigableMap的定义:

public interface NavigableMap<K,V> extends SortedMap<K,V>

再看SortedMap的定义:

public interface SortedMap<K,V> extends Map<K,V>

继承/实现关系如下:

代码示例:

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

public class TreeMapDemo1 {
    public static void main(String[] args) {
        // 按照 键 排序,默认升序
        Map<Integer, String> myMap = new TreeMap<>();
        myMap.put(2, "Anna");
        myMap.put(3, "Bob");
        myMap.put(1, "Cindy");
        System.out.println(myMap); // 输出:{1=Cindy, 2=Anna, 3=Bob}
    }
}

 

键也可以是自定义的类,前提是必须实现Comparable接口,重写comparableTo()方法定制排序规则。比如键为一个餐厅类,键值对按评分降序排序,值为按照评分获取的推荐度。

代码示例:

Canteen.java:定义一个Canteen类

public class Canteen implements Comparable<Canteen> {
    private String name;
    private int score;

    public Canteen(String name, int score) {
        this.name = name;
        this.score = score;
    }


    @Override
    public int compareTo(Canteen canteen) {
        if (this.score < canteen.score) {
            return 1;
        }else if (this.score > canteen.score) {
            return -1;
        }else {
            return this.name.compareTo(canteen.name);
        }
    }

    @Override
    public String toString() {
        return "Canteen{" +
                "name='" + name + '\'' +
                ", score=" + score +
                '}';
    }

    public String recommend(int score) {
        if (score >=9) {
            return "强烈推荐";
        }else if (score >=5) {
            return "推荐";
        }else {
            return "不推荐";
        }
    }

    public int getScore() {
        return score;
    }
}

 TreeMap.java:展示排序结果

package kaikeba2;

import java.util.Map;
import java.util.TreeMap;

public class TreeMapDemo2 {
    public static void main(String[] args) {
        // 先创建几个Canteen的对象
        Canteen can1 = new Canteen("麦德基",3);
        Canteen can2 = new Canteen("华莱士",7);
        Canteen can3 = new Canteen("肯德基", 10);
        Canteen can4 = new Canteen("麦当劳", 9);

        // 将这几个类存到Map中,存放时不是降序的
        Map<Canteen, String> myMap = new TreeMap<>();
        myMap.put(can1, can1.recommend(can1.getScore()));
        myMap.put(can2, can2.recommend(can2.getScore()));
        myMap.put(can3, can3.recommend(can3.getScore()));
        myMap.put(can4, can4.recommend(can4.getScore()));

        // 打印myMap,观察排序效果
        System.out.println(myMap);
    }
}

运行结果:{Canteen{name='肯德基', score=10}=强烈推荐, Canteen{name='麦当劳', score=9}=强烈推荐, Canteen{name='华莱士', score=7}=推荐, Canteen{name='麦德基', score=3}=不推荐}

3 Iterator

Iterator包含一个子接口ListIterator,这是集合遍历输出的首选接口,利用Collection中的iterator()方法将集合转化为迭代器,然后通过迭代器遍历元素。

Iterator接口定义为:public interface Iterator<E>,用于迭代一个集合,从而实现集合的遍历。

定义了三个方法:hasNext() next() remove()

遍历的原理是:判断当前元素是否具有下一个元素,有就输出,如此循环,直到下一个元素为null。

ListIterator接口的定义为:public interface ListIterator<E> extends Iterator<E>,能够实现双向输出

代码示例1:Iterator

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;

public class IteratorDemo {
    public static void main(String[] args) {
        Collection<String> myList = new ArrayList<>();
        myList.add("Sun");
        myList.add("Moon");
        myList.add("Earth");
        // 用Collection自带的iterator()方法将 集合 转化为 迭代器
        Iterator<String> myIter = myList.iterator();
        while (myIter.hasNext()) { // 判断下一个元素是否存在
            String ele = myIter.next(); // 取出下一个元素
            System.out.println(ele);
            if(ele.equals("Earth")) {
                myIter.remove(); // 删除元素时必须调用定义在Iterator里面的方法
                // myList.remove(); 这个操作是错误的
            }
        }
        System.out.println(myList); // 输出:[Sun, Moon],Eearth被删掉了
     }
}

 代码示例2:ListIterator

import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;

public class ListIteratorDemo {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
        myList.add("Sun");
        myList.add(0, "Moon");
        myList.add("Earth");
        ListIterator<String> myIter = myList.listIterator();
        // 从前向后输出
        while(myIter.hasNext()) {
            System.out.println(myIter.next());
        }
        System.out.println("-------------------------");
        // 从后向前输出
        while(myIter.hasPrevious()){
            System.out.println(myIter.previous());
        }
        // 反向遍历之前必须先实现一次正向遍历,否则无法实现
    }
}

4 其他集合遍历输出的方法

4.1 for循环遍历

只有List类能直接用for循环遍历输出,Set类不能被直接遍历。

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

public class ArrayListDemo2 {
    public static void main(String[] args) {
        List<String> myList = new ArrayList<>();
        myList.add("Sun"); 
        myList.add("Moon"); 
        myList.add("Earth");
        for(int i=0;i<myList.size();i++) {
            System.out.println(myList.get(i));
        }
    }
}

Collection类都能转化为数组。对于不能直接遍历的Set,可以采用这种方法。

import java.util.HashSet;
import java.util.Set;

public class HashSetDemo {
    public static void main(String[] args) {
        Set<String> mySet = new HashSet<>();
        mySet.add("Sun");
        mySet.add("Moon");
        mySet.add("Earth");
        mySet.add("Sun"); // 不能被添加,因为值为Sun的元素已经存在,元素值不可重复
        System.out.println(mySet);
        // 输出:[Earth, Moon, Sun],与添加时顺序并不一致
        
        // 转换为数组后循环遍历
        String[] myArray2 = mySet.toArray(String[]::new);
        // 这种写法也对: mySet.toArray(new String[] {})
        for(int i=0;i<mySet.size();i++) {
            System.out.println(myArray2[i]);
        }
    }
}

4.2 foreach循环遍历

foreach可以用于遍历所有Collection集合,不仅是List,Set也能foreach遍历

import java.util.HashSet;
import java.util.Set;

public class ForeachDemo1 {
    public static void main(String[] args) {
        // 遍历List
        List<String> myList = new ArrayList<>();
        myList.add("Sun");
        myList.add(0, "Moon");
        myList.add("Earth");
        myList.add(myList.size(), "Mars");
        for(String ele : myList){
            System.out.println(ele);
        }
        
        // 遍历Set
        Set<String> mySet = new HashSet<>();
        mySet.add("Sun");
        mySet.add("Moon");
        mySet.add("Earth");
        mySet.add("Sun");
        for (String ele:mySet) {
            System.out.println(ele);
        }
    }
}

4.3 Enumeration

Enumeration的含义是枚举,只能用于操作Vector,这一传统接口现在已经被迭代器取代,操作和迭代器Iterator类似。遍历前,用elements()将Vector转换为Enueration。

import java.util.Enumeration;
import java.util.Vector;

public class EnumerationDemo1 {
    public static void main(String[] args) {
        Vector<String> myVector = new Vector<>();
        myVector.add("Sun");
        myVector.add(0,"Moon");
        myVector.add("Earth");
        myVector.add(myVector.size(), "Mars");
        // 实例化一个Enumeration对象,将Vector转换为Enumeration
        Enumeration<String> myEnu = myVector.elements();
        while(myEnu.hasMoreElements()) {
            System.out.println(myEnu.nextElement());
        }
    }
}

 

  • 15
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 27
    评论
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

文程公子

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

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

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

打赏作者

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

抵扣说明:

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

余额充值