Set接口常用功能

Set:不包含重复元素的集合,不保证顺序。而且方法和Collection一致。Set集合取出元素的方式只有 一种:迭代器。

HashSet:哈希表结构,不同步,保证元素唯一性的方式依赖于:hashCode(),equals()方法。查询速度快。

Set方法完全来自Collection接口。

哈希表总结:保证元素的唯一,其实就是根据对象的hashCode和equals方法来决定的。如果我们往集合中存放自定义的对象,那么保证其唯一,就必须复写hashCode和equals方法建立属于当前对象的比较方式。

HashSet案例:单词统计 

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Locale;
import java.io.File;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class FileOperation {
    public static boolean readFile(String filename, ArrayList<String> words) {
        if (filename == null || words == null) {
            System.out.println("filename is null or words is null");
            return false;
        }
        Scanner scanner;
        try {
            File file = new File(filename);
            if (file.exists()) {
                FileInputStream fis = new FileInputStream(file);
                scanner = new Scanner(new BufferedInputStream(fis), "UTF-8");
                scanner.useLocale(Locale.ENGLISH);
            } else
                return false;
        } catch (IOException ioe) {
            System.out.println("Cannot open " + filename);
            return false;
        }
        if (scanner.hasNextLine()) {
            String contents = scanner.useDelimiter("\\A").next();
            int start = firstCharacterIndex(contents, 0);
            for (int i = start + 1; i <= contents.length(); )
                if (i == contents.length() || !Character.isLetter(contents.charAt(i))) {
                    String word = contents.substring(start, i).toLowerCase();
                    words.add(word);
                    start = firstCharacterIndex(contents, i);
                    i = start + 1;
                }
            else i++;
        }
        return true;
    }

    private static int firstCharacterIndex(String s, int start) {
        for (int i = start; i < s.length(); i++) if (Character.isLetter(s.charAt(i))) return i;
        return s.length();
    }
}

import java.util.ArrayList;
import java.util.HashSet;

public class CountWords {
    public static void main(String[] args) {
        System.out.println("傲慢与偏见");
        ArrayList<String> words = new ArrayList<String>();
        FileOperation.readFile("pride-and-prejudice.txt", words);
        System.out.println("总的单词数为:" + words.size());
        HashSet<String> set = new HashSet<String>();
        for (String word : words) {
            set.add(word);
        }
        System.out.println("不同单词总数:" + set.size());
    }
}

结果为:

傲慢与偏见
总的单词数为:0
不同单词总数:0

TreeSet:

基于 TreeMap 的 NavigableSet 实现。使用元素的自然顺序对元素进行排序,或者根据创建 set 时提供的 Comparator 进行排序,具体取决于使用的构造方法。 可以对Set集合中的元素进行排序。使用的是二叉树结构。

TreeSet案例:两个数组的交集

import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeSet;
public class Solution {
    public static int[] intersection(int[] nums1, int[] nums2) {
        TreeSet<Integer> set = new TreeSet<Integer>();
        for (Integer num : nums1) {
            set.add(num);
        }
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (Integer num : nums2) {
            if (set.contains(num)) {
                list.add(num);
                set.remove(num);
            }
        }
        int[] res = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            res[i] = list.get(i);
        }
        return res;
    }

    public static void main(String[] args) {
        int[] num1={1,2,3};
        int[] num2={2,3,4};
        System.out.println("两个数组的交集为:"+Arrays.toString(intersection(num1,num2)));
    }
}

结果为:

两个数组的交集为:[2, 3]

Map接口常用功能

HashTable

此类实现一个哈希表,该哈希表将键映射到相应的值。任何非 null 对象都可以用作键或值。为了成功地在哈希表中存储和获取对象,用作键的对象必须实现 hashCode 方法和 equals 方法。

HashMap

基于哈希表的 Map 接口的实现。此实现提供所有可选的映射操作,并允许使用 null 值和null 键。(除了非同步和允许使用 null 之外, HashMap 类与 Hashtable 大致相同。)此类不保证映射的顺序,特别是它不保证该顺序恒久不变。

TreeMap

基于红黑树(Red-Black tree )的 NavigableMap 实现。该映射根据其键的 自然顺序 进行排序,或者根据创建映射时提供的 Comparator 进行排序,具体取决于使用的构造方法。

ConcurrentHashMap

支持获取的完全并发和更新的所期望可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但获取操作 不 必锁定,并且 不 支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。

案例一:两个数组的交集

import java.util.ArrayList;
import java.util.Arrays;
import java.util.TreeMap;
public class Solution {
    public static int[] intersection(int[] nums1, int[] nums2) {
        TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
        for (int num : nums1) {
            if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                map.put(num, map.get(num) + 1);
            }
        }
        ArrayList<Integer> list = new ArrayList<Integer>();
        for (int num : nums2) {
            if (map.containsKey(num)) {
                list.add(num);
                map.put(num, map.get(num) - 1);
                if (map.get(num) == 0) {
                    map.remove(num);
                }
            }
        }
        int[] res = new int[list.size()];
        for (int i = 0; i < list.size(); i++) {
            res[i] = list.get(i);
        }
        return res;
    }
    public static void main(String[] args) {
        int[] num1 = {1, 2, 3, 4, 5};
        int[] num2 = {2, 3, 4, 5, 6, 7};
        System.out.println("两个数组的交集为:" + Arrays.toString(intersection(num1, num2)));
    }
}

结果为:

两个数组的交集为:[2, 3, 4, 5]

案例二:前K个高频元素

import java.util.LinkedList;
import java.util.List;
import java.util.PriorityQueue;
import java.util.TreeMap;
public class Solution {
    private class Freq implements Comparable<Freq> {
        int num, freq;

        public Freq(int num, int freq) {
            this.num = num;
            this.freq = freq;
        }

        @Override
        public int compareTo(Freq another) {
            if (this.freq > another.freq) {
                return 1;
            } else if (this.freq < another.freq) {
                return -1;
            } else {
                return 0;
            }
        }
    }

    public List<Integer> topKFrequent(int[] nums, int k) {
        TreeMap<Integer, Integer> map = new TreeMap<>();
        for (int num : nums) {
            if (!map.containsKey(num)) {
                map.put(num, 1);
            } else {
                map.put(num, map.get(num) + 1);
            }
        }
        PriorityQueue<Freq> queue = new PriorityQueue<Freq>();
        for (int num : map.keySet()) {//O(n)
            if (queue.size() < k) {
                queue.add(new Freq(num, map.get(num)));
            } else if (map.get(num) > queue.peek().freq) {
                queue.remove();
                queue.add(new Freq(num, map.get(num)));
            }
        }
        LinkedList<Integer> list = new LinkedList<>();
        while (!queue.isEmpty()) {
            list.add(queue.remove().num);
        }
        return list;
    }

    public static void main(String[] args) {
        Solution solution=new Solution();
        int[] nums={1,1,2,3,1,2,3,4,5};
        System.out.println("前三个高频词汇是:"+solution.topKFrequent(nums,3));
    }
}

结果为:

前三个高频词汇是:[2, 3, 1]