廖雪峰java教程学习笔记——集合

2020/4/7

1.List用法

List接口方法:

  • 在末尾添加一个元素:void add(E e)
  • 在指定索引添加一个元素:void add(int index, E e)
  • 删除指定索引的元素:int remove(int index)
  • 删除某个元素:int remove(Object e)
  • 获取指定索引的元素:E get(int index)
  • 获取链表大小(包含元素的个数):int size() 
  • 是否包含某个元素:boolean contains(Object o) 
  • 返回某个元素的索引,如果元素不存在,就返回-1:int indexOf(Object o)

创建List:List.of()方法不接受null值,如果传入null,会抛出NullPointerException异常

List list = List.of(1, 2, 5);

遍历List:用Iterator迭代器

 Iterator对象有两个方法:boolean hasNext()判断是否有下一个元素,E next()返回下一个元素

public class Main {
    public static void main(String[] args) {
        List list = List.of("apple", "pear", "banana");
        for (Iterator it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }

        //也可以简写为这样
        for (String s : list) {
            System.out.println(s);
        }
        
    }
}

List和Array转换:

toArray(T[])传入一个类型相同的ArrayList内部自动把元素复制到传入的Array中:

public class Main {
    public static void main(String[] args) {
        List list = List.of(12, 34, 56);
        Integer[] array = list.toArray(new Integer[3]);
        for (Integer n : array) {
            System.out.println(n);
        }
    }
}

把Array变为List,通过List.of(T...)方法:

Integer[] array = { 1, 2, 3 };
List list = List.of(array);

List.of()方法返回的是一个只读List,不能调用add(),remove()方法。

2.Equal()写法

要正确使用Listcontains()indexOf()这些方法,放入的实例必须正确覆写equals()方法

public class Person {
    public String name;
    public int age;
    //覆写equals()方法
    public boolean equals(Object o) {
    if (o instanceof Person) {
        Person p = (Person) o;
        return Objects.equals(this.name, p.name) && this.age == p.age;
    }
    return false;
    }
}

引用类型的比较,使用Objects.equals()静态方法 ,两个引用类型都是null时它们也是相等的。

3.Map用法

Map是一种键-值映射表。和List类似,Map也是一个接口,最常用的实现类是HashMap

常用方法:

  • put(K key, V value):keyvalue做了映射并放入Map
  • V get(K key):以通过key获取到对应的value。如果key不存在,则返回null
  • boolean containsKey(K key)方法:查询某个key是否存在。
  • keySet() :返回key的集合。
  • entrySet():返回key-value集合。

key-value集合可以调用getKey(),getValue()方法得到key和value。

public class Main {
    public static void main(String[] args) {
        Map map = new HashMap<>();
        map.put("apple", 123);
        map.put("pear", 456);
        map.put("banana", 789);
        for (Map.Entry entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }
    }
}

正确使用Map必须保证:

  1. 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true

  2. 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:

  • 如果两个对象相等,则两个对象的hashCode()必须相等;
  • 如果两个对象不相等,则两个对象的hashCode()尽量不要相等。

计算hashCode()的时候,经常借助Objects.hash()来计算:

int hashCode() {
    return Objects.hash(firstName, lastName, age);
}

 编写equals()hashCode()遵循的原则是:

equals()用到的用于比较的每一个字段,都必须在hashCode()中用于计算;equals()中没有使用到的字段,绝不可放在hashCode()中计算。对于放入HashMapvalue对象,没有任何要求。

4.EnumMap用法

如果作为key的对象是enum类型,那么,还可以使用Java集合库提供的一种EnumMap,它在内部以一个非常紧凑的数组存储value,并且根据enum类型的key直接定位到内部数组的索引,并不需要计算hashCode(),不但效率最高,而且没有额外的空间浪费。

public class Main {
    public static void main(String[] args) {
        Map map = new EnumMap<>(DayOfWeek.class);
        map.put(DayOfWeek.MONDAY, "星期一");
        map.put(DayOfWeek.TUESDAY, "星期二");
        map.put(DayOfWeek.WEDNESDAY, "星期三");
        map.put(DayOfWeek.THURSDAY, "星期四");
        map.put(DayOfWeek.FRIDAY, "星期五");
        map.put(DayOfWeek.SATURDAY, "星期六");
        map.put(DayOfWeek.SUNDAY, "星期日");
        System.out.println(map);
        System.out.println(map.get(DayOfWeek.MONDAY));
    }
}

5.TreeMap用法

有一种Map,它在内部会对Key进行排序,这种Map就是SortedMap。注意到SortedMap是接口,它的实现类是TreeMap

使用TreeMap时,放入的Key必须实现Comparable接口,如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

public class Main {
    public static void main(String[] args) {
        Map map = new TreeMap<>(new Comparator() {
            public int compare(Person p1, Person p2) {
                return p1.name.compareTo(p2.name);
            }
        });
        map.put(new Person("Tom"), 1);
        map.put(new Person("Bob"), 2);
        map.put(new Person("Lily"), 3);
        for (Person key : map.keySet()) {
            System.out.println(key);
        }
        // {Person: Bob}, {Person: Lily}, {Person: Tom}
        System.out.println(map.get(new Person("Bob"))); // 2
    }
}

class Person {
    public String name;
    Person(String name) {
        this.name = name;
    }
    public String toString() {
        return "{Person: " + name + "}";
    }
}

6.set用法

Set用于存储不重复的元素集合,它主要提供以下几个方法:

  • 将元素添加进Setboolean add(E e)
  • 将元素从Set删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)
  • 集合中元素个数:int size()

Set实际上相当于只存储key、不存储value的Map。我们经常用Set用于去除重复元素。因为放入Set的元素和Map的key类似,都要正确实现equals()hashCode()方法,否则该元素无法正确地放入Set

最常用的Set实现类是HashSet,实际上,HashSet仅仅是对HashMap的一个简单封装。使用TreeSet和使用TreeMap的要求一样,添加的元素必须正确实现Comparable接口,如果没有实现Comparable接口,那么创建TreeSet时必须传入一个Comparator对象。

2020/4/8

7. Queue用法

Java的标准库中,队列接口Queue定义了以下几个方法:

  • int size():获取队列长度;
  • boolean add(E)/boolean offer(E):添加元素到队尾;
  • E remove()/E poll():获取队首元素并从队列中删除;
  • E element()/E peek():获取队首元素但并不从队列中删除。
  抛出异常 返回true/false或null
入队 boolean add(E e) boolean offer(E e)
出队,获取队首元素 E remove() E poll()
获取队首元素 E element() E peek()

注意:不要把null添加到队列中,否则poll()方法返回null时,很难确定是取到了null元素还是队列为空。

LinkedList即实现了List接口,又实现了Queue接口:

// 这是一个List:
List list = new LinkedList<>();
// 这是一个Queue:
Queue queue = new LinkedList<>();

调用add()方法,当添加失败时(可能超过了队列的容量),它会抛出异常:

Queue q = ...
try {
    q.add("Apple");
    System.out.println("添加成功");
} catch(IllegalStateException e) {
    System.out.println("添加失败");
}

调用offer():

Queue q = ...
if (q.offer("Apple")) {
    System.out.println("添加成功");
} else {
    System.out.println("添加失败");
}

优先级队列PriorityQueue:

PriorityQueue实现了一个优先队列:从队首获取元素时,总是获取优先级最高的元素。

PriorityQueue默认按元素比较的顺序排序(必须实现Comparable接口),也可以通过Comparator自定义排序算法(元素就不必实现Comparable接口)

public class Main {
    public static void main(String[] args) {
        Queue q = new PriorityQueue<>(new UserComparator());
        // 添加3个元素到队列:
        q.offer(new User("Bob", "A1"));
        q.offer(new User("Alice", "A2"));
        q.offer(new User("Boss", "V1"));
        System.out.println(q.poll()); // Boss/V1
        System.out.println(q.poll()); // Bob/A1
        System.out.println(q.poll()); // Alice/A2
        System.out.println(q.poll()); // null,因为队列为空
    }
}

class UserComparator implements Comparator {
    public int compare(User u1, User u2) {
        if (u1.number.charAt(0) == u2.number.charAt(0)) {
            // 如果两人的号都是A开头或者都是V开头,比较号的大小:
            int num1 = Integer.parseInt(u1.number.substring(1, u1.number.length()));
            int num2 = Integer.parseInt(u2.number.substring(1, u2.number.length()));
            return Integer.compare(num1, num2);
        }
        if (u1.number.charAt(0) == 'V') {
            // u1的号码是V开头,优先级高:
            return -1;
        } else {
            return 1;
        }
    }
}

class User {
    public final String name;
    public final String number;

    public User(String name, String number) {
        this.name = name;
        this.number = number;
    }

    public String toString() {
        return name + "/" + number;
    }
}

双端队列Deque:

实现类有ArrayDeque和LinkedList;

Deque d2 = new LinkedList<>();
d2.offerLast("z");
  • 将元素添加到队尾或队首:addLast()/offerLast()/addFirst()/offerFirst()
  • 从队首/队尾获取元素并删除:removeFirst()/pollFirst()/removeLast()/pollLast()
  • 从队首/队尾获取元素但不删除:getFirst()/peekFirst()/getLast()/peekLast()

8.Stack用法

栈(Stack)是一种后进先出(LIFO)的数据结构,操作栈的元素的方法有:

  • 把元素压栈:push(E)
  • 把栈顶的元素“弹出”:pop()
  • 取栈顶元素但不弹出:peek()

在Java中,我们用Deque可以实现Stack的功能,注意只调用push()/pop()/peek()方法。

9.Iterator

如果我们自己编写了一个集合类,想要使用for each循环,只需满足以下条件:

  • 集合类实现Iterable接口,该接口要求返回一个Iterator对象;
  • Iterator对象迭代集合内部数据。

一个简单的Iterator示例如下,它总是以倒序遍历集合:

import java.util.*;

public class Main {
    public static void main(String[] args) {
        ReverseList rlist = new ReverseList<>();
        rlist.add("Apple");
        rlist.add("Orange");
        rlist.add("Pear");
        for (String s : rlist) {
            System.out.println(s);
        }
    }
}

class ReverseList implements Iterable {

    private List list = new ArrayList<>();

    public void add(T t) {
        list.add(t);
    }

    @Override
    public Iterator iterator() {
        return new ReverseIterator(list.size());
    }

    class ReverseIterator implements Iterator {
        int index;

        ReverseIterator(int index) {
            this.index = index;
        }

        @Override
        public boolean hasNext() {
            return index > 0;
        }

        @Override
        public T next() {
            index--;
            return ReverseList.this.list.get(index);
        }
    }
}

在编写Iterator的时候,我们通常可以用一个内部类来实现Iterator接口,这个内部类可以直接访问对应的外部类的所有字段和方法。例如,上述代码中,内部类ReverseIterator可以用ReverseList.this获得当前外部类的this引用,然后,通过这个this引用就可以访问ReverseList的所有字段和方法。

10.Collections用法

Collections类提供了一组工具方法来方便使用集合类:

addAll()方法可以给一个Collection类型的集合添加若干元素:

public static boolean addAll(Collection c, T... elements) { ... }

创建空集合:

  • 创建空List:List emptyList()
  • 创建空Map:Map emptyMap()
  • 创建空Set:Set emptySet()

返回的空集合是不可变集合,无法向其中添加或删除元素。

创建单元素集合:

  • 创建一个元素的List:List singletonList(T o)
  • 创建一个元素的Map:Map singletonMap(K key, V value)
  • 创建一个元素的Set:Set singleton(T o)

返回的单元素集合也是不可变集合,无法向其中添加或删除元素。

排序:

Collections.sort(list);

洗牌:

Collections.shuffle(list);

不可变集合

  • 封装成不可变List:List unmodifiableList(List list)
  • 封装成不可变Set:Set unmodifiableSet(Set set)
  • 封装成不可变Map:Map unmodifiableMap(Map m)
public class Main {
    public static void main(String[] args) {
        List mutable = new ArrayList<>();
        mutable.add("apple");
        mutable.add("pear");
        // 变为不可变集合:
        List immutable = Collections.unmodifiableList(mutable);
        // 立刻扔掉mutable的引用:
        mutable = null;
        System.out.println(immutable);
    }
}

 

 

 

 

 

 

 

你可能感兴趣的:(廖雪峰老师java教程学习笔记)