JavaSE(五)集合


java.util提供了集合类,包括
Collection:根接口
List:有序列表
Set:无重复元素集合
Map:通过Key查找Value的映射表
Java集合支持泛型,通过迭代器(Iterator)访问集合

Java集合设计的特点:
	1)接口和实现相分离
	2)支持泛型
	3)访问集合有统一的方法(Iterator)
JDK的部分集合类时遗留类,不应该继续使用
	1)Hashtable:一种线程安全的Map实现
	2)Vector:一种线程安全的List实现
	3)Stack:基于Vector实现的LIFO的栈
	4)Enumeration\<E>:已被Iterator\<E>取代

List

使用List

List是一种有序列表,通过索引访问元素
	1)void add(E e):在末尾添加一个元素
	2)void add(int index, E e):在指定索引位置添加一个元素
	3)int remove(int index):删除指定索引位置的元素
	4)int remove(Object e):删除某个元素
	5)E get(int index):获取指定索引的元素
	6)int size():获取链表大小(包含元素的个数)

List有ArrayList和LinkedList两种实现

ArrayList LinkedList
获取指定元素 速度很快 需要从头开始查找元素
添加元素到末尾 速度很快 速度很快
在指定位置添加/删除 需要移动元素 不要移动元素
内存占用 较大
List中的元素可以重复,元素也可以是null
遍历List使用Iterator或者foreach循环

把List转换为Array的方法
1)Object[] toArray()
2) T[] toArray(T[] a)

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
Integer[] array = list.toArray(new Integer[2]);
// {1, 2, 3}
Integer[] array = list.toArray(new Integer[5]);
// {1, 2, 3, null, null}
// 推荐===> Integer[] array = list.toArray(new Integer[list.size()]);

把Array转换为List的方法
1) List Arrays.asList(T… a)

Integer[] array = {1, 2, 3};
List<Integer> list = Arrays.asList(array);
// 注意返回的list并不是ArrayList
// list.add(4); // UnsupportedOperatationException
List<Integer> arrayList = new ArrayList<>();  // ^_^
arrayList.addAll(list);  // ^_^

编写equals方法

List是一种有序链表,List内部按照放入元素的先后顺序存放,每个元素都可以通过索引确定自己的位置

判断元素是否存在或者查找元素索引:
	1)boolean contains(Object o):是否包含某个元素
	2)int indexOf(Object o):查找某个元素的索引,不存在返回-1

要正确调用contains、indexOf方法,放入的实例要正确实现equals(),因为这两个方法底层是用equals比较实现的

equals()编写方法:
	1)判断 this == o
	2)判断 o instanceof Person
	3)强制转型,并比较每个对应的字段
		基本类型字段用 == 直接比较
		引用类型字段借助Objects.equals()判断
public class Person {
	private String name;

	private int age;

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

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

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

	public boolean equals(Object o) {
		if (this == o) {
			return true;
		}
		if (o instanceof Person) {
			Person p = (Person) o;
			// return (p.name==this.name || p.name!=null &&
			// p.name.equals(this.name)) && p.age==this.age;
			return Objects.equals(p.name, this.name) && p.age == this.age;
		}
		return false;
	}
}
public class Main {
	public static void main(String[] args) {
		List<Person> list = new ArrayList<>();
		list.add(new Person("aa", 12));
		list.add(new Person("bb", 34));
		list.add(new Person("cc", 56));
		System.out.println(list);
		System.out.println(list.contains(new Person("cc", 56))); // true
	}
}

Map

使用Map

Map是一种键值映射表,可以通过Key快速查找Value
常用方法:
	1)V put(K key, V value):把Key-Value放入Map
	2)V get(K key):通过Key获取Value
	3)boolean containsKey(K key):判断Key是否存在
遍历Map用for...each循环:
	1)循环Key:keySet()
	2)循环Key和Value:entrySet()
常用的实现类:
	HashMap:不保证有序(遍历时的顺序不一定是put放入时的顺序,也不一定是Key的排序顺序)
	SortedMap:保证按Key排序,实现类有TreeMap

TreeMap按元素顺序排序,可以自定义排序算法

Map<String, Integer> map = new TreeMap<>(new Comparator<String>(){
	public int compare(String s1, String s2){
		return -s1.compareTo(s2); // 按字母倒序
	}
})

编写equals和hashCode

正确使用Map必须保证:
	1)作为Key的对象必须正确覆写了equals()方法
	2)作为Key的对象必须正确覆写了hashCode()方法
覆写hashCode:
	1)如果两个对象相等,则两个对象的hashCode必须相等
	2)如果两个对象不相等,则两个对象的hashCode尽量不相等(可能相等,会造成效率下降)
hashCode可以通过Objects.hashCode()辅助方法实现
@Override
public int hashCode() {
	return Objects.hash(this.name, this.age);
}

@Override
public boolean equals(Object obj) {
	if(obj == this){
		return true;
	}
	if(obj instanceof Person){
		Person p = (Person) obj;
		return Objects.equals(this.name, p.name) && this.age==p.age;
	}
	return false;
}

public static void main(String[] args) {
	List<Person> list = Arrays.asList(
			new Person("aa", 12), 
			new Person("bb", 34), 
			new Person("cc", 56));
	Map<Person, String> map = new HashMap<>();
	for(Person p : list){
		map.put(p, p.getName());
	}
	System.out.println(map.get(new Person("cc", 56)));
}

使用Properties

Properties用于读取配置
	1).properties文件只能使用ASCII编码,可以从文件系统和ClassPath读取
	2)读取对个.properties文件,后读取的Key-Value会覆盖已读取的Key-Value
Properties实际上是从Hashtable派生,但只需调用getProperty和setProperty
String getProperty(String key); // √
void setProperty(String key, String value);	// √
Object get(Object key);	// ×
void put(Object key, Object value);	// ×

Set

使用Set

Set用于存储不重复的元素集合:
	1)boolean add(E e)
	2)boolean remove(Object o)
	3)boolean contains(Object o)
	4)int size()
利用Set可以去除重复元素
放入Set的元素要正确实现equals()和hashCode()
Set不保证有序:
	HashSet是无序的
	TreeSet是有序的(与TreeMap类似,可以自定义排序算法)
实现了SortedSet接口的是有序Set

练习

请将List的元素去重,但保留元素在List中的原始顺序,即:
[“abc”, “xyz”, “abc”, “www”, “edu”, “www”, “abc”];
去重是应该删除:
[“abc”, “xyz”, “abc” , “www”, “edu”, “www” , “abc” ];
所以结果为
[“abc”, “xyz”, “www”, “edu”];
提示:LinkedHashSet

public static void main(String[] args) {
	List<String> list = Arrays.asList("abc", "xyz", "abc", "www", "edu", "www", "abc");
	System.out.println(list); // [abc, xyz, abc, www, edu, www, abc]
	Set<String> set = new LinkedHashSet<>(); 
	for(String s : list){
		set.add(s);
	}
	System.out.println(set); // [abc, xyz, www, edu]
}

Queue

使用Queue

队列(Queue)是一种先进先出(FIFO)的数据结构
实现类:ArrayDeque、LinkedList
操作Queue的元素的方法:
	1)添加至队尾压栈:add()、offer()
	2)获取队列头部元素并删除:E remove()、E poll()
	3)获取队列头部元素但不删除:E element()、E peek()
	4)获取队列长度:size()

两组方法的区别,是当添加或获取元素失败时:

throw Exception 返回false或null
添加元素到队尾 add(E e) boolean offer(E e)
取队首元素并删除 E remove() E poll()
取队首元素但不删除 E element() E peek()

避免把null添加到队列

扫描二维码关注公众号,回复: 4834707 查看本文章

使用PriorityQueue

PriorityQueue的出队顺序与元素的优先级有关
	从队首取元素,总是获取优先级最高的元素
	默认按元素比较的顺序排序(必须实现Comparable接口)
	可以通过Comparator自定义排序算法(不必实现Comparable接口)

Deque

Deque实现一个双端队列(Double Ended Queue)
	1)既可以添加到队尾,也可以添加到队首
	2)既可以从队首获取,又可以从队尾获取
	3)添加元素到队尾:addLast(E e)、offerLast(E e)
	4)取队首元素并删除:E removeFirst()、E pollFirst()
	5)取队首元但不删除:E getFirst()、E peekFirst()
总是调用xxxFirst、xxxLast以便与Queue的方法区分开

Deque的实现类:ArrayDeque,LinkedList

Deque<String> obj = new LinkedList<>();
obj.offerLast("z"); // √

LinkedList<String> obj = new LinkedList<>();
obj.offerLast("z"); // ×

List<String> obj = new LinkedList<>();
obj.add("z"); // √

Stack

栈(Stack)是一种后进先出(LIFO)的数据结构
操作栈的元素的方法:
	入栈:push(E e)
	出栈:pop()
	取出栈顶元素但是不出栈:peek()
Java使用Deque实现栈的功能,注意只调用push/pop/peek,避免调用Deque的其他方法
不要使用遗留类Stack

Stack的作用
方法(函数)的嵌套调用
嵌套调用过多会造成栈溢出StackOverflowError
将中缀表达式编译为后缀表达式

public class Calculator {
	public Object[] compile(String s) throws IllegalAccessException {
		Object[] parsed = parseAsExpression(s);
		List<Object> output = new LinkedList<>();
		Deque<Character> stack = new LinkedList<>();
		for (Object e : parsed) {
			if (e instanceof Integer) {
				output.add(e);
			} else {
				char ch = (Character) e;
				switch (ch) {
				case ')':
					for (;;) {
						if (stack.isEmpty()) {
							throw new IllegalAccessException("Compile error "
									+ s);
						}
						char top = stack.pop();
						if (top == '(') {
							break;
						} else {
							output.add(top);
						}
					}
					break;
				case '(':
					stack.push(ch);
					break;
				case '+':
				case '-':
				case '*':
				case '/':
					while (!stack.isEmpty()) {
						char first = stack.peek();
						if (priority(first) >= priority(ch)) {
							stack.pop();
							output.add(first);
						} else {
							break;
						}
					}
					stack.push(ch);
					break;
				default:
					throw new IllegalAccessException("Compile error: " + s);
				}
			}
		}
		while (!stack.isEmpty()) {
			output.add(stack.pop());
		}
		return output.toArray();
	}

	public int calculate(Object[] expression) {
		Deque<Integer> stack = new LinkedList<>();
		for (Object e : expression) {
			if (e instanceof Integer) {
				stack.push((Integer) e);
			} else {
				char op = (Character) e;
				int n1 = stack.pop();
				int n2 = stack.pop();
				int r = operate(op, n2, n1);
				stack.push(r);
			}
		}
		return stack.pop();
	}

	Object[] parseAsExpression(String s) throws IllegalAccessException {
		List<Object> list = new ArrayList<>();
		for (char ch : s.toCharArray()) {
			if (ch >= '0' && ch <= '9') {
				int n = Integer.parseInt(String.valueOf(ch));
				list.add(n);
			} else if ("+-*/()".indexOf(ch) != (-1)) {
				list.add(ch);
			} else if (ch == ' ') {
				// ignore white space
			} else {
				throw new IllegalAccessException(
						"Compile error: invalid char \'" + ch + "\'");
			}
		}
		return list.toArray();
	}

	// priority from high to low: '*', '/' > '+', '-' > '('
	int priority(char op) throws IllegalAccessException {
		switch (op) {
		case '*':
		case '/':
			return 2;
		case '+':
		case '-':
			return 1;
		case '(':
			return 0;
		default:
			throw new IllegalAccessException("bad operator: " + op);
		}
	}

	int operate(char operator, int a, int b) {
		switch (operator) {
		case '+':
			return a + b;
		case '-':
			return a - b;
		case '*':
			return a * b;
		case '/':
			return a / b;
		default:
			throw new UnsupportedOperationException();
		}
	}
}
public class Main {
	public static void main(String[] args) throws IllegalAccessException {
		String s = "1 + 2 * (9 - 5)";
		Calculator calc = new Calculator();
		Object[] exp = calc.compile(s);
		int result = calc.calculate(exp);
		System.out.println("[calculate] " + s + " => " + expressionToString(exp) + " => " + result);
	}
	static String expressionToString(Object[] exp){
		List<String> list = new ArrayList<>(exp.length);
		for(Object e : exp){
			list.add(e.toString());
		}
		return String.join(" ", list);
	}
}
// [calculate] 1 + 2 * (9 - 5) => 1 2 9 5 - * + => 9

练习
请用Stack将整数转化为十六进制字符串表示,即:toHex(12500) ⇒ “30d4”
进制转换算法:
1)不断对整数除以16,得到商和余数,余数入栈
2)用得到的商重复步骤1
3)当商为0时,计算结束。
将栈中的数依次弹出并组成String

最佳实践

Iterator迭代

for...each循环是编译器实现的Iterator模式
适用于for...each循环的类:
	1)实现Iterable接口
	2)返回Iterator实例
好处:
	1)对任何集合都采用同一种访问模型
	2)调用者对集合内部结构一无所知
	3)集合类返回的Iterator对象知道如何迭代
	4)Iterator是一种抽象的数据访问模型

Collections

Collections是JDK提供的集合工具类
创建空集合(不可变):emptyList、emptySet、emptyMap
创建单元素集合(不可变):singleton、singletonList、singletonMap
对List排序(必须传入可变的List):sort
随机重置List元素(洗牌):suffle
把可变集合变为不可变集合:unmodifiableList、unmodifiableSet、unmodifiableMap
把线程不安群集合变为线程安全集合:synchronizedList、synchronizedSet、synchronizedMap(已不推荐使用)

猜你喜欢

转载自blog.csdn.net/yangwei234/article/details/84985663
今日推荐