本文最后更新于 2024-11-21,文章距离上次更新已超30天,内容可能有些老了——

最近在被算法题和课内双重打击中——

逐渐改为用java写算法了,在从c++切换到java的时候,经常会不小心记错方法名...

所以写一个博客来记录一下java中的集合类的常用方法!之后还有其他类的话可能会继续写——

1.List


List<E> 的接口:
boolean add(int index , E e) // 在指定index的位置添加一个元素
boolean add(E,e) // 在某位添加一个元素

E remove(int index)  //删除指定索引处的元素
boolean remove(Object e) //删除某一个元素
E get(int index) // 获得指定索引处的元素
int size() 获取链表大小(包含元素个数)

boolean contains(Object o) // 判断List是否包含某个指定元素
int indexOf(Object o) //返回某个元素的索引,如果元素不存在,返回-1

// 这里contains,indexOf内部比较是通过equals方法实现的
    java中String等都已经默认覆写了equals方法。
    如果传入的是一个自定义的类,需要自己再覆写一下equals方法

List 接口的实现类型:
    ArrayList
    LinkedList

//通过创建以上的接口实例,可以添加null
    List<T> list = new ArrayList<>();
    list.add(null);

//快速创建List,通过List接口的of方法
    List<T> list = List.of(1,2,3,4);

//遍历list 1
    for(int i = 0 ; i<list.size;i++){
        <T> cur = list.get(i);
    } // 不推荐,如果list是LinkedList的实例实现,则这里访问效率低

//遍历list 2 -- 使用迭代器
    迭代器本身也是一个对象 Iterator , 通过调用list.iterator() 来创建
    for(Iterator<T> it = list.iterator();it.hasNext();){
       T cur = it.next; // 调用it.next 的时候,它就自动向后移动了
    }
    // 迭代器只适合单向移动,且不能直接修改集合中的元素 ,需要使用迭代器的remove方法。

//遍历list3 -- for-each 循环(这里效果同2,jvm会将for-each循环变为Iterator调用
    for(T cur: list){
       System.out.println(cur);
    }

// 从List 转化为 数组Array
    T[] array = list.toArray(new T[]);
    T[] array2= list.toArray(T[]::new);
// 从Array转化为List
    List<T> list = List.of(array);
    但是调用list.of()返回的,不是具体的LinkedList或者ArrayList
    而是一个只读List(考虑到List本身只是一个接口)--不能使用add,remove方法

2.Map

Map<K,V> map = new HashMap<>();

//Map<K,V> 接口:
V put((K)key , (V)value); // 如果key本身已经存在,put方法会返回被删除的旧的value,加入新的value。如果key本身不存在,加入新的value,返回null
V get((K)key); // 通过key查找返回value。如果没有找到,返回null
boolean containsKey((K)key);

//如果是自己设计的key类 -- 使用get的时候,需要判断key是否相同
    这里的key类的equals() 方法需要自己进行覆写。
    注意,如果equals中使用的字段,必须覆写到hashcode方法中
    这里也是利用了Objects库中的方法(它不是Object。)

//遍历Map
    1.for-each 循环 -- 利用Map实例的keySet()方法,返回不重复的key的集合
    for(K key : map.keySet()){
        V value = map.get(key);
    }

    2.如果需要同时遍历key,value -- 利用Map对象的entrySet()集合
    for(Map.Entry<K,V> entry : map.entrySet()){
        K key = entry.getKey();
        V value = entry.getValue();
    }
    // 但是遍历Map不保证顺序!这点和List不同。


//关于覆写hashcode,equals -- 基本都是针对key对象,对于value对象没有要求。
    boolean equals(Object o ){
        if( o instanceof T){ //这里T是该变量的类。
            T E = (T)o; //强制类型转化
            // 假定T类有两个需要比较的量,一个是引用类型变量a,一个是基本类型变量b
            return Objects.equals(this.a,E.a) && this.b == E.b;
        }
        return false;
    }

    int hashcode(){
        return Objects.hash(a,b); // 要将在equals出现的所有变量都加到hash中。
        //确保每一个不同的对象尽可能有一个不同的hash值,一个对象本身的hash值要始终相同
    }

//EnumMap -- 枚举类map 。 通常对一个枚举类使用。 -- 感觉就是对枚举类,翻译成一个个类似c++中pair类的数组
    Map<K,V> map = new EnumMap<>(K.class);
    // 由于EnumMap内部会根据k中key的数量创建一个紧凑数组,k是枚举类
    // 而java中泛型的实现是基于擦除法,无法通过T来获得类型。所以需要再最后再传入K.class
    map.put(K.ENUM1,value1); ... 再将enum的值一个个加入..

    当key的对象是enum类型的时候可以使用,比hashmap效率高,且没有空间浪费。

//TreeMap -- key是有序的的map。
    放入的key类必须实现comparable 接口。String,Integer这些类已经实现了,可以直接作为key使用
    如果key类的class没有实现Comparable接口,则必须创建TreeMap的时候指定排序算法(传入comparator)
    Map<K,V> map = new TreeMap<>(new Comparator<K>(){
        public int compare(K k1,K k2){
            //进行比较。
            //注意,当两者相同的时候,要返回0。否则TreeMap查找不正常
            -- 即,要正确实现:大于,小于,等于 这三个逻辑。
        }
    })

    TreeMap 没有使用,不用覆写equals和hashCode。
    -- hashMap是根据equals来判断是否相等
    -- 而TreeMap是根据compare来判断是否相等的。

3.Set

Set<T> 接口

boolean add(E e);  //添加元素 如果元素不存在,加入该元素并返回true,反之返回false添加失败
boolean remove(Object e); // 删除元素 若不存在该元素,返回false,删除失败
boolean contains(Object e); // 判断是否包含元素
int size();

具体实现类:
HashSet     无序 -- 基于HashMap实现,所以需要equals和hashCode的覆写
    Set<T> set = new HashSet<>();
TreeSet     有序 -- 基于SortedSet接口,需要实现Comparable接口,或者传入Comparator对象
    Set<T> set = new TreeSet<>();

4.Queue and Deque

Queue<T> 接口:
int size();
boolean add(E) / boolean offer(E)  //将元素加到对尾
E remove() / E poll()  //获取队首元素并从队列中删除
E element() / E peek() //获取队首元素,但不删除

前面的三个方法,在失败的时候,抛出异常。 而后面的三个,失败的时候返回false或者null,不会抛出异常。


具体实现类:
LinkedList<T>
    //LinkedList既实现了List接口,也实现了Queue接口。
    List<T> list = new LinkedList<>() ;
    // 返回的是一个List类型
    Queue<T> que = new LinkedList<>();
    // 返回的是一个Queue类型。


PriorityQueue<T> 优先级队列
    默认按照元素比较顺序排序 -- 需要实现Comparable接口,或者通过Comparator自定义排序算法
    Queue<T> que = new PriorityQueue<>(); // 默认顺序
    Queue<T> que = new PriorityQueue<>(new myComparator()); // 这里myComparator类需要单独写



Deque<t> 接口:
boolean addLast(E e)    / offerLast(E e)
E removeFirst() / E pollFirst()
E getFirst()    / E peekFirst()
boolean addFirst(E e)   / offerFirst(E e)
E removeLast()  / E pollLast()
E getLast()     / E peekLast()



具体实现类: (它本身是扩展自Queue的)
LinkedList

ArrayDeque

5.Stack

Stack<T> 接口:

push(E)
pop()
peek()

---- 用Deque模拟Stack ----
push(E) ---> addFirst(E)
pop()   ---> pollFirst()
peek()  ---> peekFirst()

将Deque作为Stack使用的时候,只需要调用push,pop,peek方法即可,不用写后面那种
使得代码更清晰
------------------------

由于java中Stack与一个遗留类重名,所以不推荐直接用Stack,而是使用Deque来模拟栈
    Deque<T> stack = new LinkedList<>();