Java编程思想 第11章持有对象 学习笔记
其他
2021-03-02 07:06:03
阅读次数: 0
基本概念
- Java容器类类库的用途:保存对象,划分为两个不同的概念:
– Collection
:一个独立元素的序列,这些元素都服从一条或多条规则。List
必须按照插入的顺序保存元素,Set
不能有重复的元素。Queue
按照排队规则来确定对象产生的顺序。
– Map
:一组成对的“键值对”对象,允许你使用键来查找值。映射表允许我们使用另一个对象来查找某个对象,它也被称为“关联数组”,因为它将某些对象与另外一些对象关联在一起。
- 创建
List
List<Apple> apples = new ArrayList<>();
创建一个具体的类,将其转型为对应的接口,,然后其余的代码中都使用这个接口。
但是,如果要使用该接口额外的方法,就不能将具体的类向上转型为该接口。
- 添加元素
public class SimpleCollection {
public static void main(String[] args) {
Collection<Integer> c = new ArrayList<Integer>();
for(int i = 0; i < 10; i++) {
c.add(i);
}
for(Integer i: c) {
System.out.print(i + ", ");
}
}
}
- 添加一组元素
Collection.addAll()
成员方法只能接受另一个Collection
,用它将自己初始化,因此可以通过Arrays.List()
来为这个构造器输入。但是,Collections.addAll()
方法运行起来快得多,构建一个空的Collection
,然后调用Collections.addAll()
这种方式很方便,因此它是首选方式。
可以直接使用Arrays.adList()
输出,将其当作List
,但这种情况下,底层是数组,没有重写add()
、delete()
方法,因此不能进行修改操作,否则会报异常。public class AddingGroups {
public static void main(String[] args) {
Collection<Integer> collection = new ArrayList<Integer>(Arrays.asList(1, 2, 3, 4, 5));
Integer[] moreInts = {
6, 7, 8, 9, 10};
collection.addAll(Arrays.asList(moreInts));
Collections.addAll(collection, 11, 12, 13, 14, 15);
Collections.addAll(collection, moreInts);
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
List.set(1, 99);
}
}
List
- 两种类型的
List
– ArrayList
:长于随机访问元素,但是在List
中间插入和移除元素时较慢。
– LinkedList
:在List
中间插入和删除操作代价较低,提供了优化的顺序访问。但是随机访问相对较慢。List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(1);
list.add(2);
list.add(3);
list.remove(Integer.valueOf(1));
list.remove(1);
System.out.println(list.contains(3));
System.out.println(list.get(0));
System.out.println(list.indexOf(1));
list.add(5);
list.add(6);
list.add(7);
list.add(8);
System.out.println(list);
System.out.println(list.subList(1, 4));
System.out.println(list.containsAll(Arrays.asList(1, 5, 3)));
list.add(1);
list.add(-1);
list.add(-7);
list.add(-8);
Collections.sort(list);
list.sort(Comparator.reverseOrder());
Collections.shuffle(list);
list.removeAll(Arrays.asList(1, 3));
list.set(1, 8);
list.addAll(Arrays.asList(1, 2));
list.addAll(1, Arrays.asList(4, 5));
list.retainAll(Arrays.asList(1, 2, 3, 4, 6));
Object[] objects = list.toArray();
Integer[] integers = list.toArray(new Integer[0]);
list.clear();
System.out.println(list.isEmpty());
– 移除一个元素remove()
,是用equals
来查找对象的。其他查找的方法,也是使用equals
的
– 当存在很多从中间插入的操作时add(int index, Integer element)
、addAll(int index, Collection<? extends Integer> c)
,考虑使用LinkedList
而不是ArrayList
– subList()
所产生的列表的幕后就是初始列表,因此对返回的列表的修改都会反映到初始列表中,反之亦然。
– retainAll()
是一种有效的“交集”操作,并且会将结果保存在list
中
– toArray()
方法,将任意的Collection()
转换为一个数组。无参数版本返回的是Object
数组,如果传递指定类型的数组,则会返回该类型的数组(内部将创建一个合适尺寸的数组保存结果)
迭代器
- 迭代器是一个对象,它的工作是遍历并选择序列中的对象,而客户端程序员并不需要关心该序列底层的结构。此外,迭代器通常是轻量级对象:创建它的代价小。
– 使用iterator()
要求容器返回一个Iterator
,Iterator
将准备好返回序列的第一个元素
– 使用next()
获得序列的下一个元素
– 使用hasNext()
检查序列中是否还有元素
– 使用remove()
将新近返回的元素删除(即next()
刚返回的对象)。调用remove()
之前必须调用next()
– display()
方法不包含任何有关它所遍历的序列的类型信息,能够将遍历序列的操作与序列底层的结构分离。因此,迭代器统一了对容器的访问方式。List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9);
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
iterator = list.iterator();
for (int i = 0; i < 3; i++) {
System.out.println(iterator.next());
iterator.remove();
}
System.out.println(list);
public void display(Iterator<?> iterator) {
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
ListIterator
– ListIterator
是一个更加强大的Iterator
的子类型。可以双向移动还可以指向当前位置的前一个索引和后一个索引。
– 还可以使用set()
方法替换它访问的最后一个元素
– 可以在开始导出iterator()
的时候,加入参数index
,实现一开始就指向第n个元素。List<Integer> list = new ArrayList<Integer>();
Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9);
ListIterator<Integer> listIterator = list.listIterator();
while (listIterator.hasNext()) {
System.out.println("next():" + listIterator.next() + " nextIndex():" + listIterator.nextIndex() +
" previousIndex():" + listIterator.previousIndex());
}
while (listIterator.hasPrevious()) {
System.out.println("previous():" + listIterator.previous());
}
listIterator = list.listIterator(3);
while (listIterator.hasNext()) {
System.out.println("next():" + listIterator.next() + " nextIndex():" + listIterator.nextIndex() +
" previousIndex():" + listIterator.previousIndex());
}
LinkedList
LinkedList
还添加了用作栈、队列和双端队列的方法
- 返回列表第一个元素:
getFirst()
和element()
完全一致,当List
为空的时候,抛出NoSuchElementException
异常。peek()
方法也类似,只是当列表为空时返回null
- 删除并返回第一个元素:
removeFirst
和remove()
完全移植,移除并返回第一个元素,当List
为空的时候,抛出NoSuchElementException
异常。poll()
稍有差异,列表为空时返回null
- 在尾部添加元素:
add()
和addFirst()
完全一样,在列表尾部加入元素
LinkedList
的add()
和remove()
有点像队列?先进先出。
Stack
Set
Set
不保存重复的元素。Set
最常被使用的是测试归属性,你可以很容易地询问某个对象是否在某个Set
中。正因如此,查找就成为了Set
中最重要的操作。因此你通常都会选择一个HashSet
的实现,它专门对快速查找进行优化。
Set
具有与Collection
完全一样的接口,因此没有任何额外的功能。只是行为不同(体现了继承性与多态性)Set<Integer> set = new HashSet<Integer>();
Random rand = new Random();
for (int i = 0; i < 1000; i++) {
set.add(rand.nextInt(30));
}
System.out.println(set);
- 处于速度原因的考虑,
HashSet
使用了散列。TreeSet
存储在红黑树数据结构中。LinkedHashMap
因为查询速度的原因也使用了散列,但是看起来它使用了链表来维护元素的插入顺序。
- 如果想对结果排序,一种方法是使用
TreeSet
Set<Integer> set = new TreeSet<>();
set.add(53);
set.add(1);
set.add(4);
set.add(-14);
set.add(4);
set.add(434);
set.add(5);
System.out.println(set);
contains()
测试Set
的归属性Set<String> set = new HashSet<>();
Collections.addAll(set, "4 4 523 54 fsf 3532 54".split(" "));
System.out.println(set);
System.out.println(set.contains("4"));
Map
- 将对象映射到其他对象
Random rand = new Random(47);
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < 10000; i++) {
int r = rand.nextInt(20);
Integer freq = map.get(r);
map.put(r, freq == null ? 1 : freq + 1);
}
System.out.println(map);
containsKey()
和containsValue()
Map<String, String> map = new HashMap<>();
map.put("cat", "ketty");
map.put("dog", "honey");
System.out.println(map.containsKey("cat"));
System.out.println(map.containsValue("ketty"));
Map
很容易拓展成多维。可以创建一个Map<Person, List<Pet>>
的Map,每个值保存一个List
- 获取所有键、值、键值对
Set<String> strings = map.keySet();
Collection<String> values = map.values();
Set<Map.Entry<String, String>> entries = map.entrySet();
System.out.println(strings);
System.out.println(values);
for (Map.Entry<String, String> entry : entries) {
String key = entry.getKey();
String value = entry.getValue();
System.out.println(key + " " + value);
}
Queue
- 队列是一个典型的先进先出(FIFO)的容器。LinkedList提供了以支持队列的行为,并且它实现了Queue接口,因此LinkedList用作Queue的一种实现。
offer()
方法,它在允许情况下,将一个元素插入到队尾,或者返回false
。peek()
和element()
都将在不移除的情况下,返回队头,但是peek()
方法在队列为空的时候返回null
,element()
会抛出NoSuchElementException
异常。
poll()
和remove()
都会移除队头并返回。在队列为空时,poll()
会返回空,remove()
会抛出NoSuchElementException
异常。Queue<Integer> queue = new LinkedList<>();
for (int i = 0; i < 10; i++) {
queue.offer(i);
}
System.out.println(queue.peek());
while (!queue.isEmpty()) {
System.out.println(queue.poll());
}
- 优先级队列
– 优先级队列声明下一个弹出元素时最需要的元素(具有最高优先级)。
– 当你在PriorityQueue
上调用offer()
方法来插入一个对象时,这个对象会在队列中排序,默认的排序将使用对象在队列中的自然顺序(从小到大),但是可以提供自己的Comparator
来修改这个顺序。
– PriorityQueue
可以确保当你调用peek()
、poll()
、remove()
方法时,获取的元素将是队列中优先级最高的元素。List<Integer> input = Arrays.asList(54, 3, 4, 5, 5, 45, 7, 5, 8);
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
priorityQueue.addAll(input);
System.out.println(priorityQueue);
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll());
}
priorityQueue = new PriorityQueue<>(Collections.reverseOrder());
priorityQueue.addAll(input);
System.out.println(priorityQueue);
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll());
}
PriorityQueue<String> priorityQueue = new PriorityQueue<>(new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return o1.length()- o2.length();
}
});
priorityQueue.offer("amy");
priorityQueue.offer("amyfaf");
priorityQueue.offer("fadf");
priorityQueue.offer("fadfa");
while (!priorityQueue.isEmpty()) {
System.out.println(priorityQueue.poll());
}
Collection和Iterator
Collection
是描述所有序列容器的共性的根接口。
- 使用接口描述的一个理由是它可以使我们能够创建更通用的代码。通过针对接口而非具体实现来编写代码。
Collection
和Iterator
均可以表示容器之间的共性public static <T> void display(Iterator<T> iterator) {
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
public static <T> void display(Collection<T> collection) {
for (T c : collection) {
System.out.println(c);
}
}
两个版本的display()
方法都可以及那个display()
方法与底层容器的特定实现解耦。
- 当你要实现一个不是
Collection
的外部类时,由于让它实现Collection
接口可能非常困难或麻烦,因此使用Iterator
就变得非常吸引人。实现Collection
必须实现所有的Collection
方法,即使不需要使用某些方法。而Iterator
只需要实现两个方法。
public interface Iterator<E> {
boolean hasNext();
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
Foreach与迭代器
- 之所以
foreach
能够工作,是因为实现了Iterable
接口,该接口包含一个能够产生Iterator
的iterator()
方法,并且Iterable
接口被foreach
用来在序列中移动
- 因此,创建的任何实现了
Iterable
的类,都可以通过foreach
遍历Collection<String> cs = new ArrayList<>();
cs.addAll(Arrays.asList("faf fae gs hfdh".split(" ")));
for(String s : cs) {
System.out.println(s);
}
public class IterableImpl implements Iterable<String> {
String[] s = "faf gdagfa aff gag".split(" ");
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < s.length;
}
@Override
public String next() {
return s[index++];
}
};
}
@Test
public void test() {
IterableImpl strings = new IterableImpl();
for (String string : strings) {
System.out.println(string);
}
}
}
Collection
接口继承了Iterable
接口,从而实现了Iterable
接口的几个方法
Arrays.asList()
- 意识到Arrays.asList()产生的List对象会使用底层数组作为物理实现是很重要的。只要你执行的操作会修改这个List,并不想原来的数组被修改,那么你就应该在另一个容器上中创建一个副本。
Random rand = new Random();
Integer[] ia = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
List<Integer> list1 = new ArrayList<Integer>(Arrays.asList(ia));
Collections.shuffle(list1, rand);
List<Integer> list2 = Arrays.asList(ia);
Collections.shuffle(list2, rand);
转载自blog.csdn.net/weixin_42524843/article/details/113854084