集合

使用List

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

List还允许添加null

 		List<String> list = new ArrayList<>();
        list.add("apple"); // size=1
        list.add("pear"); // size=2
        list.add("apple"); // 允许重复添加元素,size=3
        System.out.println(list.size());

创建List

//java9
List<Integer> list = List.of(1, 2, 5);
//java8
List<Integer> list = Arrays.asList(1, 2, 5);

  • Arrays.asList返回可变的list,而List.of返回的是不可变的list
  • Arrays.asList支持null,而List.of不行
  • Arrays.asList:数组的修改会影响原数组。

坚持使用迭代器Iterator来访问List

  		List<String> list = Arrays.asList("apple", "pear", "banana");
        for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
            String s = it.next();
            System.out.println(s);
        }
或者
   		List<String> list = Arrays.asList("apple", "pear", "banana");
        for (String s : list) {
            System.out.println(s);
        }

List和Array转换

//Array  ===>list
 Number[] array = list.toArray(new Number[3]);
Integer[] array = list.toArray(new Integer[list.size()]);
Integer[] array = list.toArray(Integer[]::new);
//list  ===>Array
Integer[] array = { 1, 2, 3 };
List<Integer> list = Arrays.asList(array);
   // 洗牌算法shuffle可以随机交换List中的元素位置:
    Collections.shuffle(list);
	list.contains(integer)//检查list集合是否包含integer元素

编写equals方法

  • 自反性(Reflexive):对于非null的x来说,x.equals(x)必须返回true;
  • 对称性(Symmetric):对于非null的x和y来说,如果x.equals(y)为true,则y.equals(x)也必须为true;
  • 传递性(Transitive):对于非null的x、y和z来说,如果x.equals(y)为true,y.equals(z)也为true,那么x.equals(z)也必须为true;
  • 一致性(Consistent):对于非null的x和y来说,只要x和y状态不变,则x.equals(y)总是一致地返回true或者false;
  • 对null的比较:即x.equals(null)永远返回false。
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;
}

使用Map

put(K key, V value)
V get(K key)
boolean containsKey(K key). 只是想查询某个key是否存在

//遍历Map
  		for (String key : map.keySet()) {
            Integer value = map.get(key);
            System.out.println(key + " = " + value);
        }
        或者
        for (Map.Entry<String, Integer> entry : map.entrySet()) {
            String key = entry.getKey();
            Integer value = entry.getValue();
            System.out.println(key + " = " + value);
        }

Map中不存在重复的key,因为放入相同的key,只会把原有的key-value对应的value给替换掉。
遍历Map时,不可假设输出的key是有序的!

编写equals和hashCode

正确使用Map必须保证:

  • 作为key的对象必须正确覆写equals()方法,相等的两个key实例调用equals()必须返回true;
  • 作为key的对象还必须正确覆写hashCode()方法,且hashCode()方法要严格遵循以下规范:
  • 如果两个对象相等,则两个对象的hashCode()必须相等;
  • 如果两个对象不相等,则两个对象的hashCode()尽量不要相等。
  @Override
int hashCode() {
    return Objects.hash(firstName, lastName, age);
}
或者
  @Override
    int hashCode() {
        int h = 0;
        h = 31 * h + firstName.hashCode();
        h = 31 * h + lastName.hashCode();
        h = 31 * h + age;
        return h;
    }

实际上HashMap初始化时默认的数组大小只有16,加超过一定数量的key-value时,HashMap会在内部自动扩容,每次扩容一倍,即长度为16的数组扩展为长度32

更好的方式是创建HashMap时就指定容量
Map<String, Integer> map = new HashMap<>(10000);

我们把不同的key具有相同的hashCode()的情况称之为哈希冲突。在冲突的时候,一种最简单的解决办法是用List存储hashCode()相同的key-value。显然,如果冲突的概率越大,这个List就越长,Map的get()方法效率就越低。
如果两个对象不相等,则两个对象的hashCode()尽量不要相等。

使用EnumMap

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

使用TreeMap

使用TreeMap时,放入的Key必须实现Comparable接口。String、Integer这些类已经实现了Comparable接口,因此可以直接作为Key使用。作为Value的对象则没有任何要求。
如果作为Key的class没有实现Comparable接口,那么,必须在创建TreeMap时同时指定一个自定义排序算法:

   Map<Person, Integer> map = new TreeMap<>(new Comparator<Person>() {
            public int compare(Person p1, Person p2) {
                return p1.name.compareTo(p2.name);
            }
        });

使用Properties

用Properties读取配置文件,一共有三步:
创建Properties实例;
调用load()读取文件;
调用getProperty()获取配置。

调用setProperty()写入配置。

String f = "setting.properties";
Properties props = new Properties();
props.load(new java.io.FileInputStream(f));

String filepath = props.getProperty("last_open_file");
String interval = props.getProperty("auto_save_interval", "120");

//也可以从classpath读取.properties
Properties props = new Properties();
props.load(getClass().getResourceAsStream("/common/setting.properties"));

## 写入
Properties props = new Properties();
props.setProperty("url", "http://www.liaoxuefeng.com");
props.setProperty("language", "Java");
props.store(new FileOutputStream("C:\\conf\\setting.properties"), "这是写入的properties注释");

使用Set

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

  • 将元素添加进Set:boolean add(E e)
  • 将元素从Set删除:boolean remove(Object e)
  • 判断是否包含元素:boolean contains(Object e)

Set接口并不保证有序,而SortedSet接口则保证元素是有序的:

  • HashSet是无序的,因为它实现了Set接口,并没有实现SortedSet接口;
  • TreeSet是有序的,因为它实现了SortedSet接口。

使用Queue

队列(Queue)是一种经常使用的集合。Queue实际上是实现了一个先进先出(FIFO:First In First Out)的有序表。它和List的区别在于,List可以在任意位置添加和删除元素,而Queue只有两个操作:

  • 把元素添加到队列末尾;
  • 从队列头部取出元素。
int size():获取队列长度;
boolean add(E)/boolean offer(E):添加元素到队尾;
E remove()/E poll():获取队首元素并从队列中删除;
E element()/E peek():获取队首元素但并不从队列中删除。
throw Exception返回false或null
添加元素到队尾add(E e)boolean offer(E e)
取队首元素并删除E remove()E poll()
取队首元素但不删除E element()E peek()

要避免把null添加到队列。

优先队列PriorityQueue

放入PriorityQueue的元素,必须实现Comparable接口,PriorityQueue会根据元素的排序顺序决定出队的优先级。

//eg
public class Main {
    public static void main(String[] args) {
        Queue<User> 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 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;
    }
}
class UserComparator implements Comparator<User> {
    public int compare(User u1, User u2) {
        if (u1.number.charAt(0) == u2.number.charAt(0)) {
            // 如果两人的号都是A开头或者都是V开头,比较号的大小:
                if(u1.number.length() < u2.number.length()) {return -1;}  // 比较number长度,短的优先

            if(u1.number.length() > u2.number.length()) {return  1;} // 比较number长度,短的优先

            return u1.number.compareTo(u2.number);                      // 长度一致时,再用compareTo比较字符串
        }
        if (u1.number.charAt(0) == 'V') {
            // u1的号码是V开头,优先级高:
            return -1;
        } else {
            return 1;
        }
    }
}

使用Deque双端队列

QueueDeque
添加元素到队尾add(E e)/offer(E e)addLast(E e) / offerLast(E e)
取队首元素并删除E remove() /E poll()E removeFirst() / E pollFirst()
取队首元素但不删除E element()/E peek()E getFirst() / E peekFirst()
添加元素到队首addFirst(E e) / offerFirst(E e)
取队尾元素并删除E removeLast() / E pollLast()
取队尾元素但不删除E getLast() / E peekLast()
   public static void main(String[] args) {
        Deque<String> deque = new LinkedList<>();
        deque.offerLast("A"); // A
        deque.offerLast("B"); // B -> A
        deque.offerFirst("C"); // B -> A -> C
        System.out.println(deque.pollFirst()); // C, 剩下B -> A
        System.out.println(deque.pollLast()); // B
        System.out.println(deque.pollFirst()); // A
        System.out.println(deque.pollFirst()); // null
    }

使用Stack

栈(Stack)是一种后进先出(LIFO:Last In First Out)的数据结构。

Stack只有入栈和出栈的操作:
把元素压栈:push(E);
把栈顶的元素“弹出”:pop(E);
取栈顶元素但不弹出:peek(E)。

在Java中,我们用Deque可以实现Stack的功能:
把元素压栈:push(E)/addFirst(E);
把栈顶的元素“弹出”:pop(E)/removeFirst();
取栈顶元素但不弹出:peek(E)/peekFirst()public static String toHexString(int i)转化为16进位
 boolean isEmpty(); 判断集合是否为空
// 转十六进制
 public static void main(String[] args) {
        String hex = toHex(12500);
        if (hex.equalsIgnoreCase("30D4")) {
            System.out.println("测试通过");
        } else {
            System.out.println("测试失败");
        }
    }
    static String toHex(int a) {
        Deque<String> dd = new LinkedList<>();
        int x = 0;
        for(int k=0;k<10;k++){
            x = a%16;
            a = a/16;
            if(x < 10){
                dd.push(String.valueOf(x));
            }else{
                switch(x){
                    case 10:
                        dd.push("a");
                        continue;
                    case 11:
                        dd.push("b");
                        continue;
                    case 12:
                        dd.push("c");
                        continue;
                    case 13:
                        dd.push("d");
                        continue;
                    case 14:
                        dd.push("e");
                        continue;
                    case 15:
                        dd.push("f");
                        continue;
                }
            }
            if(a==0)break;
        }
        return dd.pop() + dd.pop() + dd.pop() + dd.pop();
    }
//请利用Stack把字符串中缀表达式编译为后缀表达式,然后再利用栈执行后缀表达式获得计算结果:
public class Main {

    public static void main(String[] args) {

// --------- stack 中缀表达式编译为后缀表达式 ---------------------

        String exp = "1 + 2 * (9 - 5)";

        SuffixExpression se = compile(exp);

        Float result = se.execute();

        System.out.println(exp + " = " + result + " " + (result == 1 + 2 * (9 - 5) ? "✓" : "✗"));

    }

    static SuffixExpression compile(String exp) {
    // TODO:中缀转后缀
    
    // 1.按次序读取中缀表达式的字符。
    
    // 2.读到一个操作数的时候,立即放入到输出中。
    
    // 3.读到操作符“+”,“-”,“*”,“/”,则从栈中弹出栈元素并输出,直到遇到优先级更低或者“(”的为止操作符为止(该元素不出栈)。
    
    // 4.读到操作符“(”,则直接把“(”压入栈中。
    
    // 5.读到操作符“)”,则从栈中弹出栈元素并输出,直到遇到第一个“(”为止。其中“(”不再添加到输出中,而是直接舍弃。
    
    // 6.当输入为空时,把栈里的操作符全部依次弹出并输出。

        Deque<Character> rs= new LinkedList<>();

        char[] cexp = exp.toCharArray();

        String out = "";

        for(int i = 0; i < cexp.length; i++) {

            char ch = cexp[i];

            if (ch == ' ') continue;

            if (ch >= '0' && ch <= '9') {

                out += ch;

                continue;

            }

            if (ch == '(') {

                rs.push(ch);

            }

            if (ch == '+' || ch =='-') {

                while(!rs.isEmpty() && (rs.peek() != '(')) {

                    out += rs.pop();

                }

                rs.push(ch);

                continue;

            }

            if (ch == '*' || ch =='/') {

                while(!rs.isEmpty() && (rs.peek() == '*' || rs.peek() == '/')) {

                    out += rs.pop();

                }

                rs.push(ch);

                continue;

            }

            if (ch == ')') {

                while(!rs.isEmpty() && rs.peek() != '(') {

                    out += rs.pop();

                }

                rs.pop();

                continue;

            }

        }

        while(!rs.isEmpty()) out += rs.pop();

        System.out.println(out);

        return new SuffixExpression(out);

    }

}

class SuffixExpression {

    String exp;

    public SuffixExpression(String exp) {

        this.exp = exp;

    }

    public float execute() {

//TODO:根据后缀表达式 计算结果

// 1.按次序读取后缀表达式的每一个字符。

// 2.读取到操作数时,把操作数压入栈中。

// 3.读取到操作符时,对栈顶的2个操作数做相应运算,要注意操作数的前后顺序。结果压入栈中。

// 4.读取完所有的字符后,弹出栈。得到的值就是所求结果。

        Deque<Float> cs= new LinkedList<>();

        char[] cexp = exp.toCharArray();

        for (int i = 0; i < cexp.length; i++) {

            char ch = cexp[i];

            if (ch>='0' && ch<='9') {

                cs.push(Float.valueOf(ch - '0'));

                continue;

            } else {

                cs.push(calculate(ch, cs.pop(), cs.pop()));

            }

        }

        System.out.println(exp);

        return  cs.pop();

    }

    public float calculate(char op, Float f1, Float f2) {

        if (op == '+') return f1 + f2;

        else if (op == '-') return f2 -f1;

        else if (op == '*') return f1 * f2;

        else if (op == '/') return f2/f1;

        else return Float.valueOf(-0);

    }

}

使用Iterator

//把这种通过Iterator对象遍历集合的模式称为迭代器。
for (Iterator<String> it = list.iterator(); it.hasNext(); ) {
     String s = it.next();
     System.out.println(s);
}

Iterator是一种抽象的数据访问模型。使用Iterator模式进行迭代的好处有:

  • 对任何集合都采用同一种访问模型;
  • 调用者对集合内部结构一无所知;
  • 集合类返回的Iterator对象知道如何迭代。
//一个简单的Iterator示例如下,它总是以倒序遍历集合:
import java.util.*;
public class Main {
    public static void main(String[] args) {
        ReverseList<String> rlist = new ReverseList<>();
        rlist.add("Apple");
        rlist.add("Orange");
        rlist.add("Pear");
        for (String s : rlist) {
            System.out.println(s);
        }
    }
}

class ReverseList<T> implements Iterable<T> {

    private List<T> list = new ArrayList<>();

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

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

    class ReverseIterator implements Iterator<T> {
        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);
        }
    }
}

使用Collections

创建空集合

collections提供了一系列方法来创建空集合:
创建空List:List emptyList()
创建空Map:Map<K, V> emptyMap()
创建空Set:Set emptySet()

用各个集合接口提供的of(T...)方法创建空集合。例如,以下创建空List的两个方法是等价的:
        List<String> list1 = Arrays.asList();
        List<String> list2 = Collections.emptyList();

创建单元素集合

Collections提供了一系列方法来创建一个单元素集合:
创建一个元素的List:List singletonList(T o)
创建一个元素的Map:Map<K, V> singletonMap(K key, V value)
创建一个元素的Set:Set singleton(T o)

List<String> list1 = Arrays.asList("apple");
List<String> list2 = Collections.singletonList("apple");
Collections.sort(list);排序
Collections.shuffle(list); 洗牌算法

不可变集合

Collections还提供了一组方法把可变集合封装成不可变集合:

  • 封装成不可变List:List unmodifiableList(List<? extends T> list)
  • 封装成不可变Set:Set unmodifiableSet(Set<? extends T> set)
  • 封装成不可变Map:Map<K, V> unmodifiableMap(Map<? extends K, ? extends V> m)

这种封装实际上是通过创建一个代理对象,拦截掉所有修改方法实现的。

线程安全集合

Collections还提供了一组方法,可以把线程不安全的集合变为线程安全的集合:

  • 变为线程安全的List:List synchronizedList(List list)
  • 变为线程安全的Set:Set synchronizedSet(Set s)
  • 变为线程安全的Map:Map<K,V> synchronizedMap(Map<K,V> m)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值