1. Введение в резюме коллекции
Рамка коллекции:
Категории структуры коллекций в Java можно разделить на Collection и Map; разница между ними:
1. Коллекция — это коллекция из одного столбца, Карта — это коллекция из двух столбцов.
2. Только серия Set в Коллекции требует уникальных элементов, ключи в Карте должны быть уникальными, а значения могут повторяться
3. Структура данных Collection для элементов, структура данных Map для ключей.
Дженерики:
Прежде чем говорить о двух основных системах сбора, давайте поговорим об дженериках, потому что они будут использоваться в следующих коллекциях; Так называемый дженерик: параметризация типа
Дженерики являются частью типа, имя класса + дженерики составляют единое целое.
При наличии универсального типа, когда он не используется, тип параметра будет автоматически повышен до типа Object.Если он снова вынут, его необходимо принудительно понизить, и может возникнуть исключение преобразования типа (ClassCaseException). происходят; Тип элементов, добавленных в направленную коллекцию, вызывает проблемы при постобработке.
Ниже приведено сравнение разницы между добавлением дженериков и их отсутствием:
package 好好学java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 不加泛型,添加和遍历
List list = new ArrayList<>();
list.add(1);
list.add("123");
list.add("hello");
Iterator it = list.iterator();
while(it.hasNext()){
// 没有添加泛型,这里只能使用Object接收
Object obj = it.next();
System.out.println(obj);
}
}
}
package 好好学java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
public static void main(String[] args) {
// 加泛型,添加和遍历
List<String> list = new ArrayList<String>();
list.add("123");
list.add("hello");
Iterator<String> it = list.iterator();
while(it.hasNext()){
// 因为添加了泛型,就说明集合中装的全部都是String类型的数据
// 所以这里用String类型接收,就不会发生异常,并且可以使用String的方法
String str = it.next();
System.out.println(str.length());
}
}
}
Пользовательский класс с дженериками:
package 好好学java;
public class Test {
// 自定义一个带有一个参数的泛型类,可以向传入什么类型就传入什么类型
public static void main(String[] args) {
// 进行测试, 传入一个String对象
Person<String> perStr = new Person<String>();
perStr.setT("我是字符串");
String str = perStr.getT();
System.out.println(str);
// 进行测试,传入一个Integer对象
Person<Integer> perInt = new Person<Integer>();
perInt.setT(100);
Integer intVal = perInt.getT();
System.out.println(intVal);
}
}
//自定义一个带有一个参数的泛型类
class Person<T>{
private T t;
void setT(T t){
this.t = t;
}
T getT(){
return t;
}
}
Реализуйте тип интерфейса с помощью дженериков:
При реализации интерфейса указывается универсальный тип в интерфейсе (определяется при определении класса);
public class GenericImpl1 implements GenericInter<String> {}
При реализации интерфейса общий тип в интерфейсе не указывается. В этом случае класс реализации интерфейса должен быть определен как универсальный класс. Тип интерфейса должен быть действительно определен, когда объект класса реализации создан (тип всегда неопределен, пока тип не будет определен при создании объекта);
public class GenericImpl2<T> implements GenericInter<T> {}
Общий подстановочный знак (?):
Верхний предел: например, при определении метода,public void getFunc(List<? extends Animal> an),
Тогда это означает, что параметры здесь могут быть переданы в Animal или в подкласс Animal.
Нижний предел: например, при определении методаpublic void getFunc(Set<? super Animal> an ),
Тогда это означает, что параметры здесь могут быть переданы в Animal, или в родительский класс Animal
Примечания по использованию дженериков:
1. Дженерики не поддерживают базовые типы данных
2. Дженерики не поддерживают наследование и должны быть непротиворечивыми (например, это неправильно:List<Object> list = new ArrayList<String>();
Система сбора:
коллекция включает в себя две системы,List和Set
Особенности списка:
Доступ упорядочен, есть индекс, значение можно брать по индексу, элементы могут повторяться
Особенности набора:
Неупорядоченный доступ, элементы не могут повторяться
List:
Ниже приведеныArrayList,LinkedList,Vector
(устаревший)
Самая большая цель коллекции — доступ; Коллекция списков характеризуется упорядоченным доступом, который может хранить повторяющиеся элементы и может использовать индексы для выполнения операций с элементами.
ArrayList:Нижний слой реализован с использованием массивов, поэтому скорость запроса высокая, а скорость добавления и удаления низкая.
package 好好学java;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Test {
// 使用ArrayList进行添加和遍历
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
list.add("接口1");
list.add("接口2");
list.add("接口3");
// 第一种遍历方式,使用迭代器
Iterator<String> it = list.iterator();
while(it.hasNext()){
String next = it.next();
System.out.println(next);
}
System.out.println("-------------------");
// 第二种遍历方式,使用foreach
for (String str : list){
System.err.println(str);
}
}
}
LinkedList: он реализован на основе структуры связанного списка, поэтому скорость запроса низкая, скорость добавления и удаления высокая, и предоставляется специальный метод для работы (добавление, удаление, проверка) элементов в начале и конце.
Используйте LinkedList для реализации стеков и очередей; стеки работают в порядке очереди, а очереди — в порядке поступления.
package com.xiaoshitou.classtest;
import java.util.LinkedList;
/**
* 利用LinkedList来模拟栈
* 栈的特点:先进后出
* @author Beck
*
*/
public class MyStack {
private LinkedList<String> linkList = new LinkedList<String>();
// 压栈
public void push(String str){
linkList.addFirst(str);
}
// 出栈
public String pop(){
return linkList.removeFirst();
}
// 查看
public String peek(){
return linkList.peek();
}
// 判断是否为空
public boolean isEmpty(){
return linkList.isEmpty();
}
}
package 好好学java;
public class Test {
public static void main(String[] args) {
// 测试栈
StackTest stack = new StackTest();
stack.push("我是第1个进去的");
stack.push("我是第2个进去的");
stack.push("我是第3个进去的");
stack.push("我是第4个进去的");
stack.push("我是第5个进去的");
// 取出
while (!stack.isEmpty()){
String pop = stack.pop();
System.out.println(pop);
}
// 打印结果
/*我是第5个进去的
我是第4个进去的
我是第3个进去的
我是第2个进去的
我是第1个进去的*/
}
}
LinkedList реализует очередь:
package 好好学java;
import java.util.LinkedList;
/**
* 利用linkedList来实现队列
* 队列: 先进先出
* @author Beck
*
*/
public class QueueTest {
private LinkedList<String> link = new LinkedList<String>();
// 放入
public void put(String str){
link.addFirst(str);
}
// 获取
public String get(){
return link.removeLast();
}
// 判断是否为空
public boolean isEmpty(){
return link.isEmpty();
}
}
package 好好学java;
public class Test {
public static void main(String[] args) {
// 测试队列
QueueTest queue = new QueueTest();
queue.put("我是第1个进入队列的");
queue.put("我是第2个进入队列的");
queue.put("我是第3个进入队列的");
queue.put("我是第4个进入队列的");
// 遍历队列
while (!queue.isEmpty()){
String str = queue.get();
System.out.println(str);
}
// 打印结果
/*我是第1个进入队列的
我是第2个进入队列的
我是第3个进入队列的
我是第4个进入队列的*/
}
}
Vector: Поскольку он устарел, он заменен на ArrayList, у него также есть итератор, полученный через vector.elements(), и метод для определения наличия элементов и получения элементов:hasMoreElements(),nextElement()
.
package 好好学java;
import java.util.Enumeration;
import java.util.Vector;
public class Test {
public static void main(String[] args) {
Vector<String> vector = new Vector<String>();
vector.add("搜索");
vector.add("vector");
vector.add("list");
Enumeration<String> elements = vector.elements();
while (elements.hasMoreElements()){
String nextElement = elements.nextElement();
System.out.println(nextElement);
}
}
}
Set:
Характеристики коллекции Set: элементы не повторяются, доступ неупорядочен, индекс отсутствует.
Ниже коллекции Set находятся:HashSet,LinkedHashSet,TreeSet
HashSet хранит строки:
package 好好学java;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashSet来存取
Set<String> set = new HashSet<String>();
set.add("我的天");
set.add("我是重复的");
set.add("我是重复的");
set.add("welcome");
// 遍历 第一种方式 迭代器
Iterator<String> it = set.iterator();
while(it.hasNext()){
String str = it.next();
System.out.println(str);
}
System.out.println("--------------");
for (String str : set){
System.out.println(str);
}
// 打印结果,重复的已经去掉了
/*我的天
welcome
我是重复的
--------------
我的天
welcome
我是重复的*/
}
Каким образом хеш-таблица обеспечивает уникальность элементов?Хеш-таблица совместно гарантируется методами hashCode и equals.
Процесс хранимых данных хеш-таблицы (нижний слой хеш-таблицы также поддерживает массив):
Вычислите значение hashCode в соответствии с сохраненным элементом, а затем вычислите сохраненный индекс в соответствии с вычисленным значением hashCode и длиной массива; если в позиции нижнего индекса нет элемента, сохраните его напрямую; если есть элемент, используйте сохраненный индекс Элемент и элемент выполняют метод equals, если результат истинный, то такой же элемент уже есть, значит, он не существует напрямую, если результат ложный, то сохраняется в виде связанного списка.
Продемонстрируйте HashSet для хранения пользовательских объектов:
package 好好学java;
public class Person {
// 属性
private String name;
private int age;
// 构造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要让哈希表存储不重复的元素,就必须重写hasCode和equals方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
}
package 好好学java;
import java.util.HashSet;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashSet来存取自定义对象 Person
Set<Person> set = new HashSet<Person>();
set.add(new Person("张三", 12));
set.add(new Person("李四", 13));
set.add(new Person("王五", 22));
set.add(new Person("张三", 12));
// 遍历
for (Person p : set){
System.out.println(p);
}
// 结果:向集合中存储两个张三对象,但是集合中就成功存储了一个
/*Person [name=王五, age=22]
Person [name=李四, age=13]
Person [name=张三, age=12]*/
}
}
Поэтому при хранении пользовательских объектов в коллекции HashSet для обеспечения уникальности коллекции set методы hashCode и equals должны быть переписаны.
LinkedHashSet:
Он реализован на основе связанного списка и хеш-таблицы, поэтому имеет упорядоченный доступ и уникальные элементы.
package 好好学java;
import java.util.LinkedHashSet;
public class Test {
public static void main(String[] args) {
// 利用LinkedHashSet来存取自定义对象 Person
LinkedHashSet<Person> set = new LinkedHashSet<Person>();
set.add(new Person("张三", 12));
set.add(new Person("李四", 13));
set.add(new Person("王五", 22));
set.add(new Person("张三", 12));
// 遍历
for (Person p : set){
System.out.println(p);
}
// 结果:向集合中存储两个张三对象,但是集合中就成功存储了一个,
// 并且存进的顺序,和取出来的顺序是一致的
/*Person [name=张三, age=12]
Person [name=李四, age=13]
Person [name=王五, age=22]*/
}
}
Набор деревьев:
**Возможности:** Неупорядоченный доступ, уникальные элементы и возможность сортировки (сортировка выполняется при добавлении).
TreeSet — это структура данных на основе бинарного дерева, бинарное дерево: не более двух узлов под одним узлом.
Хранимая процедура для бинарного дерева:
Если это первый элемент, он будет сохранен непосредственно как корневой узел, а следующий элемент будет сравниваться с узлом. Поступают следующие элементы и сравниваются по очереди, пока не найдется место для их хранения
В коллекции TreeSet хранятся объекты String.
package 好好学java;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
TreeSet<String> treeSet = new TreeSet<String>();
treeSet.add("abc");
treeSet.add("zbc");
treeSet.add("cbc");
treeSet.add("xbc");
for (String str : treeSet){
System.out.println(str);
}
// 结果:取出来的结果是经过排序的
/*
abc
cbc
xbc
zbc*/
}
}
TreeSet гарантирует уникальность элементов двумя способами:
1. Пользовательский объект реализует интерфейс Comparable и переписывает метод comparaTo.Этот метод возвращает 0 для обозначения равенства, а меньше 0 указывает на то, что сохраняемый элемент меньше сравниваемого элемента, в противном случае он больше 0;
2. При создании TreeSet передайте объект класса реализации интерфейса Comparator в конструктор и реализуйте интерфейс Comparator, чтобы переопределить метод сравнения.
Если пользовательский объект хранится в TreeSet, пользовательский класс не реализует интерфейс Comparable или компаратор Comparator не передается, возникнет исключение ClassCastException.
Ниже приведена демонстрация двух способов хранения пользовательских объектов.
package 好好学java;
public class Person implements Comparable<Person>{
// 属性
private String name;
private int age;
// 构造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要让哈希表存储不重复的元素,就必须重写hasCode和equals方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
@Override
public int compareTo(Person o) {
int result = this.age - o.age;
if (result == 0){
return this.name.compareTo(o.name);
}
return result;
}
}
package 好好学java;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
// 利用TreeSet来存储自定义类Person对象
TreeSet<Person> treeSet = new TreeSet<Person>();
// Person类实现了Comparable接口,并且重写comparaTo方法
// 比较规则是先按照 年龄排序,年龄相等的情况按照年龄排序
treeSet.add(new Person("张山1", 20));
treeSet.add(new Person("张山2", 16));
treeSet.add(new Person("张山3", 13));
treeSet.add(new Person("张山4", 17));
treeSet.add(new Person("张山5", 20));
for (Person p : treeSet){
System.out.println(p);
}
// 结果:按照comparaTo方法内的逻辑来排序的
/*
Person [name=张山3, age=13]
Person [name=张山2, age=16]
Person [name=张山4, age=17]
Person [name=张山1, age=20]
Person [name=张山5, age=20]
*/
}
}
Другой способ: использовать Comparator
package 好好学java;
public class Person{
// 属性
private String name;
private int age;
// 构造方法
public Person() {
super();
}
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
// 要让哈希表存储不重复的元素,就必须重写hasCode和equals方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Person other = (Person) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
// getter & setter
...
}
package 好好学java;
import java.util.Comparator;
import java.util.TreeSet;
public class Test {
public static void main(String[] args) {
// 利用TreeSet来存储自定义类Person对象
// 创建TreeSet对象的时候传入Comparator比较器,使用匿名内部类的方式
// 比较规则是先按照 年龄排序,年龄相等的情况按照年龄排序
TreeSet<Person> treeSet = new TreeSet<Person>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2){
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0){
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
treeSet.add(new Person("张山1", 20));
treeSet.add(new Person("张山2", 16));
treeSet.add(new Person("张山3", 13));
treeSet.add(new Person("张山4", 17));
treeSet.add(new Person("张山5", 20));
for (Person p : treeSet){
System.out.println(p);
}
// 结果:按照compara方法内的逻辑来排序的
/*
Person [name=张山3, age=13]
Person [name=张山2, age=16]
Person [name=张山4, age=17]
Person [name=张山1, age=20]
Person [name=张山5, age=20]
*/
}
}
Резюме компаратора:
Краткое описание системы сбора:
-
Список: «Возможности:» Доступ упорядочен, элементы имеют индексы, и элементы могут повторяться.
-
ArrayList: структура массива, быстрый запрос, медленное добавление и удаление, небезопасный поток, поэтому высокая эффективность.
-
Вектор: структура массива, быстрый запрос, медленное добавление и удаление, безопасность потоков, поэтому низкая эффективность.
-
LinkedList: структура связанного списка, медленный запрос, быстрое добавление и удаление, небезопасность потоков, поэтому высокая эффективность.
addFirst() removeFirst() getFirst()
-
Set : «Features:» Доступ неупорядочен, элементы не имеют индекса, и элементы не могут повторяться.
-
HashSet : Хранилище неупорядочено, элементы не имеют индекса, и элементы не могут повторяться.Нижний слой представляет собой хеш-таблицу.
Извините: как хеш-таблица гарантирует уникальность элементов?Нижний слой опирается на методы hashCode и equals.
При сохранении элемента сначала вычислите индекс на основе хэш-кода + длины массива, чтобы определить, есть ли элемент в позиции индекса.
Если элемента нет, сохраните его напрямую. Если элемент есть, сначала оцените метод equals, чтобы сравнить, являются ли два элемента одинаковыми. Если они разные, сохраните их, а если они одинаковы, отбросьте их.
Элементы нашего хранилища пользовательских объектов должны реализовывать hashCode и equals.
-
LinkedHashSet: хранится по порядку, элементы не могут повторяться.
-
TreeSet: Доступ неупорядочен, но его можно сортировать (естественная сортировка), и элементы не могут повторяться.
Существует два метода сортировки:
- Естественный порядок:
Наш элемент должен реализовать интерфейс Comparable Comparable Реализовать метод CompareTo.
- Сортировка по компаратору:
Нам нужен пользовательский класс, который реализует интерфейс Comparetor.Этот класс является компаратором, который реализует метод сравнения.
Затем при создании TreeSet передайте объект сравнения в качестве параметра TreeSet.
Map:
Карта представляет собой двухколоночную коллекцию, в которой хранятся пары ключ-значение, ключи должны быть уникальными, а значения могут повторяться.
Между ключом и значением существует однозначное соответствие, а ключ может соответствовать только одному значению.
**Особенности карты: ** неупорядоченный доступ, повтор ключей невозможен.
Когда карта сохраняется, значение ключа передается в запись, а затем сохраняется объект записи.
Среди них нижеHashMap,LinkedHashMap和TreeMap
Хэш-карта:
Он реализован на основе структуры хеш-таблицы, поэтому при хранении пользовательских объектов в качестве ключей методы hasCode и equals необходимо переписать. расстройство доступа
Далее демонстрируется HashMap с пользовательскими объектами в качестве ключей:
package 好好学java;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set;
public class Test {
public static void main(String[] args) {
// 利用HashMap存储,自定义对象Person作为键
// 为了保证键的唯一性,必须重写hashCode和equals方法
HashMap<Person,String> map = new HashMap<Person,String>();
map.put(new Person("张三", 12), "JAVA");
map.put(new Person("李四", 13), "IOS");
map.put(new Person("小花", 22), "JS");
map.put(new Person("小黑", 32), "PHP");
map.put(new Person("张三", 12), "C++");
Set<Entry<Person, String>> entrySet = map.entrySet();
Iterator<Entry<Person, String>> it = entrySet.iterator();
while (it.hasNext()){
Entry<Person, String> entry = it.next();
System.out.println(entry.getKey() + "---" + entry.getValue());
}
// 结果:存入的时候添加了两个张三,如果Map中键相同的时候,当后面的值会覆盖掉前面的值
/*
Person [name=李四, age=13]---IOS
Person [name=张三, age=12]---C++
Person [name=小黑, age=32]---PHP
Person [name=小花, age=22]---JS
*/
}
}
LinkedHashMap:
Использование в основном такое же, как и у HashMap.Он основан на связном списке и структуре хеш-таблицы, поэтому имеет характеристики упорядоченного доступа и неповторяющихся ключей.
В следующей демонстрации используется хранилище LinkedHashMap, обратите внимание, что порядок хранения соответствует порядку обхода:
package 好好学java;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
public class Test {
public static void main(String[] args) {
// 利用LinkedHashMap存储,自定义对象Person作为键
// 为了保证键的唯一性,必须重写hashCode和equals方法
LinkedHashMap<Person,String> map = new LinkedHashMap<Person,String>();
map.put(new Person("张三", 12), "JAVA");
map.put(new Person("李四", 13), "IOS");
map.put(new Person("小花", 22), "JS");
map.put(new Person("小黑", 32), "PHP");
map.put(new Person("张三", 12), "C++");
// foreach遍历
for (Entry<Person,String> entry : map.entrySet()){
System.out.println(entry.getKey()+"==="+entry.getValue());
}
// 结果:存入的时候添加了两个张三,如果Map中键相同的时候,当后面的值会覆盖掉前面的值
// 注意:LinkedHashMap的特点就是存取有序,取出来的顺序就是和存入的顺序保持一致
/*
Person [name=张三, age=12]===C++
Person [name=李四, age=13]===IOS
Person [name=小花, age=22]===JS
Person [name=小黑, age=32]===PHP
*/
}
}
TreeMap:
Сохраните настраиваемый объект в коллекции TreeMap и используйте настраиваемый объект в качестве значения ключа коллекции TreeMap. Из-за бинарного дерева, используемого в нижней части TreeMap, все данные, хранящиеся в нем, необходимо сортировать, а для сортировки объект должен иметь функцию сравнения. Класс, которому принадлежит объект, должен реализовать интерфейс Comparable. Или передайте объект интерфейса Comparator в коллекцию TreeMap.
Используйте TreeMap для хранения пользовательских объектов в качестве ключей:
package 好好学java;
import java.util.Comparator;
import java.util.Map.Entry;
import java.util.TreeMap;
public class Test {
public static void main(String[] args) {
// 利用TreeMap存储,自定义对象Person作为键
// 自定义对象实现Comparable接口或者传入Comparator比较器
TreeMap<Person,String> map = new TreeMap<Person,String>(new Comparator<Person>() {
@Override
public int compare(Person o1, Person o2) {
if (o1 == o2){
return 0;
}
int result = o1.getAge() - o2.getAge();
if (result == 0){
return o1.getName().compareTo(o2.getName());
}
return result;
}
});
map.put(new Person("张三", 12), "JAVA");
map.put(new Person("李四", 50), "IOS");
map.put(new Person("小花", 32), "JS");
map.put(new Person("小黑", 32), "PHP");
map.put(new Person("张三", 12), "C++");
// foreach遍历
for (Entry<Person,String> entry : map.entrySet()){
System.out.println(entry.getKey()+"==="+entry.getValue());
}
// 结果:存入的时候添加了两个张三,如果Map中键相同的时候,当后面的值会覆盖掉前面的值
// 注意:TreeMap 取出来的顺序是经过排序的,是根据compara方法排序的
/*
Person [name=张三, age=12]===C++
Person [name=小花, age=32]===JS
Person [name=小黑, age=32]===PHP
Person [name=李四, age=50]===IOS
*/
}
}
2. Сбор расширенной сводки
Массивы и первоклассные объекты
Независимо от типа используемого массива идентификатор массива на самом деле является дескриптором реального объекта. эти объекты сами находятся в памяти создается в "куче". Объекты кучи могут быть созданы либо «неявно» (то есть по умолчанию), либо «явно» (то есть явно заданы, с новым выражение). Часть объекта кучи (фактически единственное поле или метод, к которому мы можем получить доступ) — это элемент длины, доступный только для чтения, который сообщает Максимальное количество элементов, которые мы можем хранить в этом объекте массива. Для объектов массива синтаксис «[]» является единственным альтернативным методом доступа, который мы можем использовать.
Массивы объектов и массивы примитивных типов данных практически идентичны в использовании. Единственное отличие состоит в том, что массивы объектов содержат дескрипторы, а массивы примитивных типов данных содержат конкретные значения.
public class ArraySize {
public static void main(String[] args) {
// Arrays of objects:
Weeble[] a; // Null handle
Weeble[] b = new Weeble[5]; // Null handles
Weeble[] c = new Weeble[4];
for (int i = 0; i < c.length; i++)
c[i] = new Weeble();
Weeble[] d = { new Weeble(), new Weeble(), new Weeble() };
// Compile error: variable a not initialized:
// !System.out.println("a.length=" + a.length);
System.out.println("b.length = " + b.length);
// The handles inside the array are
// automatically initialized to null:
for (int i = 0; i < b.length; i++)
System.out.println("b[" + i + "]=" + b[i]);
System.out.println("c.length = " + c.length);
System.out.println("d.length = " + d.length);
a = d;
System.out.println("a.length = " + a.length);
// Java 1.1 initialization syntax:
a = new Weeble[] { new Weeble(), new Weeble() };
System.out.println("a.length = " + a.length);
// Arrays of primitives:
int[] e; // Null handle
int[] f = new int[5];
int[] g = new int[4];
for (int i = 0; i < g.length; i++)
g[i] = i * i;
int[] h = { 11, 47, 93 };
// Compile error: variable e not initialized:
// !System.out.println("e.length=" + e.length);
System.out.println("f.length = " + f.length);
// The primitives inside the array are
// automatically initialized to zero:
for (int i = 0; i < f.length; i++)
System.out.println("f[" + i + "]=" + f[i]);
System.out.println("g.length = " + g.length);
System.out.println("h.length = " + h.length);
e = h;
System.out.println("e.length = " + e.length);
// Java 1.1 initialization syntax:
e = new int[] { 1, 2 };
System.out.println("e.length = " + e.length);
}
}
Результат выглядит следующим образом: б.длина = 5 б[0]=нуль б[1]=нуль б[2]=нуль б[3]=нуль б[4]=нуль с.длина = 4 д.длина = 3 а.длина = 3 а.длина = 2 f.длина = 5 ф[0]=0 ф[1]=0 ф[2]=0 ф[3]=0 ф[4]=0 г.длина = 4 ч.длина = 3 е.длина = 3 е.длина = 2
Среди них массив a только что инициализируется нулевым дескриптором. В этот момент компилятор запретит нам выполнять какие-либо операции с этим дескриптором, если мы не правильно инициализировал. Массив b инициализируется так, чтобы указывать на массив дескрипторов Weeble, но на самом деле этот массив не содержит никаких Weeble объект. Однако мы все еще можем запросить размер этого массива, поскольку b указывает на допустимый объект.
Другими словами, мы знаем только размер или емкость объекта массива, а не то, сколько элементов он на самом деле содержит.
Тем не менее, поскольку объекты массива автоматически инициализируются нулевым значением при их создании, его можно проверить на нуль, чтобы определить, содержит ли конкретный «слот» массива объект. Так же,Массивы примитивных типов данных автоматически инициализируются нулем (для числовых типов), null (для символьных типов) или false (для логических типов).
Массив c показывает, что мы сначала создаем объект массива, а затем назначаем объекты Weeble всем «пустым» элементам этого массива. массив d показывает синтаксис «инициализация коллекции», создание объекта массива (явно с помощью новой команды, похожей на массив c), а затем с объектом Weeble Инициализация, вся работа выполняется в одном операторе. Следующее выражение:
a = d;
Показывает нам, как получить дескриптор того же соединения с объектом массива и назначить его другому объекту массива Показывает нам, как получить дескриптор того же соединения объекта массива и назначить его другому объекту массива
1. Сбор основных типов данных Классы коллекций могут содержать только дескрипторы объектов. Но для массива можно сделать так, чтобы он непосредственно содержал данные базового типа, а также мог содержать оператор, указывающий на объект. ручка. Используя классы-обертки, такие как Integer, Double и т. д., значения примитивных типов данных можно поместить в коллекцию.
При помещении данных базовых типов в массив или их инкапсуляции в класс, расположенный в коллекции, возникает вопрос эффективности выполнения. показывать Конечно, если вы можете создать и получить доступ к массиву примитивных типов данных, это намного эффективнее, чем доступ к набору инкапсулированных данных.
возврат массива
Предположим, теперь мы хотим написать метод, и мы хотим, чтобы он возвращал не одну вещь, а серию вещей. На данный момент такие языки, как C и C++, усложняют ситуацию, потому что мы не можем возвращать массив, а только указатель на массив. Это очень хлопотно, потому что трудно контролировать «время существования» массива, и это легко может привести к появлению «дыр» в памяти.
Java использует аналогичный подход, но мы можем «вернуть массив». Конечно,На данный момент фактический возврат по-прежнему является указателем на массив. Но в Java нам никогда не нужно беспокоиться о доступности этого массива — он будет там автоматически, пока он нам нужен. И сборщик мусора автоматически очистит его, когда мы закончим
public class IceCream {
static String[] flav = { "Chocolate", "Strawberry", "Vanilla Fudge Swirl",
"Mint Chip", "Mocha Almond Fudge", "Rum Raisin", "Praline Cream",
"Mud Pie" };
static String[] flavorSet(int n) {
// Force it to be positive & within bounds:
n = Math.abs(n) % (flav.length + 1);
String[] results = new String[n];
int[] picks = new int[n];
for(int i = 0; i < picks.length; i++)
picks[i] = -1;
for(int i = 0; i < picks.length; i++) {
retry:
while(true) {
int t =(int)(Math.random() * flav.length);
for(int j = 0; j < i; j++)213
if(picks[j] == t) continue retry;
picks[i] = t;
results[i] = flav[t];
break;
}
}
return results;
}
public static void main(String[] args) {
for (int i = 0; i < 20; i++) {
System.out.println("flavorSet(" + i + ") = ");
String[] fl = flavorSet(flav.length);
for (int j = 0; j < fl.length; j++)
System.out.println("\t" + fl[j]);
}
}
}
Метод flavorSet() создает массив строк с именем results. Размер этого массива равен n — точное число зависит от аргументов, которые мы передаем методу. Затем он случайным образом выбирает некоторые «ароматы» из массива flav, помещает их в результаты и, наконец, возвращает результаты. Возврат массива ничем не отличается от возврата любого другого объекта — все, что вы возвращаете, — это дескриптор.
С другой стороны, обратите внимание, что когда flavorSet() случайным образом выбирает вкусы, она должна гарантировать, что случайный выбор, который произошел ранее, не повторится снова. Для этого используется бесконечный цикл while, который продолжает делать случайные выборки до тех пор, пока не найдет элемент, которого нет в массиве picks (конечно, можно также выполнить сравнение строк, чтобы проверить, есть ли случайный выбор в результатах). массив) появился, но эффективность сравнения строк относительно низкая). В случае успеха добавьте этот элемент, разорвите цикл и ищите следующий (я буду увеличивать). Но если t — это массив, который уже присутствует в выборках, используйте помеченное продолжение, чтобы перейти на два уровня назад, заставив выбрать новый t. Этот процесс хорошо виден с помощью отладчика.
собирать
Для хранения набора объектов наиболее подходящим выбором должен быть массив. И если он содержит ряд базовых типов данных, необходимо использовать массив.
**Минусы:**Тип неизвестен
«Недостаток» использования коллекций Java заключается в том, что вы теряете информацию о типе при помещении объектов в коллекцию. Это происходит потому, что когда коллекция была написана, программист этой коллекции понятия не имел, какой тип пользователь хотел поместить в коллекцию. Указание коллекции, разрешающей только определенные типы, не позволяет ей быть инструментом «общего назначения» и может вызвать проблемы у пользователей. Чтобы решить эту проблему, коллекции фактически содержат дескрипторы объектов типа Object.
Конечно, также обратите внимание, что коллекции не включают примитивные типы данных, потому что они не наследуются «ни от чего».Java не позволяет людям злоупотреблять объектами, помещенными в коллекции. Если вы добавите собаку в коллекцию кошек, все в коллекции по-прежнему будут рассматриваться как кошки, поэтому при использовании этой собаки вы получите ошибку «нарушение». В том же смысле, если вы попытаетесь «привязать» дескриптор собаки к кошке, вы все равно получите ошибку «нарушение» во время выполнения.
class Cat {
private int catNumber;
Cat(int i) {
catNumber = i;
}
void print() {
System.out.println("Cat #" + catNumber);
}
}
class Dog {
private int dogNumber;
Dog(int i) {
dogNumber = i;
}
void print() {
System.out.println("Dog #" + dogNumber);
}
}
public class CatsAndDogs {
public static void main(String[] args) {
Vector cats = new Vector();
for (int i = 0; i < 7; i++)
cats.addElement(new Cat(i));
// Not a problem to add a dog to cats:
cats.addElement(new Dog(7));
for (int i = 0; i < cats.size(); i++)
((Cat) cats.elementAt(i)).print();
// Dog is detected only at run-time
}
}
- Ошибки иногда не проявляются В некоторых случаях программа работает корректно, не возвращаясь к исходному типу. Первый случай довольно особенный: класс String получает дополнительную помощь от компилятора, чтобы заставить его работать. Всякий раз, когда компилятор ожидает объект String и не получает его, он автоматически вызывает метод toString(), определенный в Object, который может быть переопределен любым классом Java. Этот метод может генерировать объекты String, соответствующие требованиям, а затем использовать их, когда они нам нужны. Поэтому, чтобы отображались объекты вашего собственного класса, все, что вам нужно сделать, это переопределить метод toString().
class Mouse {
private int mouseNumber;
Mouse(int i) {
mouseNumber = i;
}
// Magic method:
public String toString() {
return "This is Mouse #" + mouseNumber;
}
void print(String msg) {
if (msg != null)
System.out.println(msg);
System.out.println("Mouse number " + mouseNumber);
}
}
class MouseTrap {
static void caughtYa(Object m) {
Mouse mouse = (Mouse) m; // Cast from Object
mouse.print("Caught one!");
}
}
public class WorksAnyway {
public static void main(String[] args) {
Vector mice = new Vector();
for(int i = 0; i < 3; i++)
mice.addElement(new Mouse(i));
for(int i = 0; i < mice.size(); i++) {
// No cast necessary, automatic call
// to Object.toString():
System.out.println(
"Free mouse: " + mice.elementAt(i));
MouseTrap.caughtYa(mice.elementAt(i));
}
}
}
Переопределение toString() можно увидеть в Mouse. Во втором цикле for функции main() можно найти следующий оператор:
System.out.println("Free mouse: " +
mice.elementAt(i));
После «+» компилятор ожидает увидеть объект String. elementAt() генерирует объект, поэтому для получения нужной строки компилятор по умолчанию вызывает toString(). Но, к сожалению, вы получите такие результаты только для String, ни один другой тип не делает такого преобразования.
Второй способ скрытия стилей был реализован в Mousetrap. Метод catchYa() получает не Мышь, а Объект. Затем сформируйте из него мышь. Конечно, делать это очень опрометчиво, потому что, получив Object, в метод можно передать что угодно. Однако, если приведение неверное — если мы передаем неправильный тип — мы получим ошибку нарушения во время выполнения. Это, конечно, не проверяется во время компиляции, но тем не менее предотвращает проблемы. Обратите внимание, что при использовании этого метода стилизация не требуется: MouseTrap.caughtYa(мыши.elementAt(i));
- Создайте вектор, который может автоматически определять тип Более «надежным» решением было бы создание нового класса из Vector, который принимает только то, что мы указываем. тип и генерировать только тот тип, который нам нужен.
class Gopher {
private int gopherNumber;
Gopher(int i) {
gopherNumber = i;
}
void print(String msg) {
if (msg != null)
System.out.println(msg);
System.out.println("Gopher number " + gopherNumber);
}
}
class GopherTrap {
static void caughtYa(Gopher g) {
g.print("Caught one!");
}
}
class GopherVector {
private Vector v = new Vector();
public void addElement(Gopher m) {
v.addElement(m);
}
public Gopher elementAt(int index) {
return (Gopher) v.elementAt(index);
}
public int size() {
return v.size();
}
public static void main(String[] args) {
GopherVector gophers = new GopherVector();
for (int i = 0; i < 3; i++)
gophers.addElement(new Gopher(i));
for (int i = 0; i < gophers.size(); i++)
GopherTrap.caughtYa(gophers.elementAt(i));
}
}
Новый класс GopherVector имеет закрытый член типа Vector (наследование от Vector немного сложнее по причинам, которые будут рассмотрены позже), а методы аналогичны Vector. Однако он не получает и не производит обычные объекты, только объекты Gopher. интересно. Так как GopherVector принимает только одного Gopher (суслика), если мы используем: gophers.addElement(новый голубь()); Во время компиляции вы получите сообщение об ошибке. Таким образом, хотя это и более утомительно с точки зрения кодирования, можно сразу определить, правильный ли тип используется. Обратите внимание, что при использовании elementAt() приведение не требуется — это должен быть Gopher.
нумератор
Первая задача коллекции — хранить самые разные предметы. В Vector метод addElement() используется для вставки объектов, а elementAt() — Единственный способ извлечь объекты. Вектор очень гибкий, мы можем выбрать что угодно в любое время и можем выбрать несколько элементов с разными индексами. Если посмотреть на эту проблему с более высокого уровня, то вы обнаружите изъян: нужно заранее знать точный тип коллекции, иначе ее нельзя будет использовать. На первый взгляд это может показаться неактуальным. Но если вы решите использовать Vector в начале, а позже решите изменить его на List (принадлежащий библиотеке-коллекции Java1.2) в программе (из соображений эффективности выполнения), что вам делать в это время? Обычно мы думаем об итераторе как о «легковесном» объекте, то есть его создание стоит очень мало. Но именно по этой причине мы часто встречаем ретрансляторы с, казалось бы, странными ограничениями. Например, некоторые повторители могут двигаться только в одном направлении. Перечисление Java (перечисление, примечание ②) является примером итератора с этими ограничениями. Не используйте его, за исключением следующих Сделайте что-нибудь еще: (1) Попросите коллекцию предоставить нам Enumeration с методом elements(). Мы вызываем его nextElement() в первый раз , это перечисление возвращает первый элемент в последовательности. (2) Используйте nextElement() для получения следующего объекта. (3) Используйте hasMoreElements(), чтобы проверить, есть ли в последовательности еще объекты.
class Hamster {
private int hamsterNumber;
Hamster(int i) {
hamsterNumber = i;
}
public String toString() {
return "This is Hamster #" + hamsterNumber;
}
}
class Printer {
static void printAll(Enumeration e) {
while (e.hasMoreElements())
System.out.println(e.nextElement().toString());
}
}
public class HamsterMaze {
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 3; i++)
v.addElement(new Hamster(i));
Printer.printAll(v.elements());
}
}
Подробнее о способе печати:
static void printAll(Enumeration e) {
while(e.hasMoreElements())
System.out.println(
e.nextElement().toString());
}
Обратите внимание, что информация о типе последовательности отсутствует. Все, что у нас есть, это перечисление. Чтобы узнать о последовательности, достаточно Enumeration: получить следующий объект и узнать, достигнут ли конец. Взятие ряда объектов и обход их для выполнения определенного действия — ценная концепция программирования.
тип коллекции
V e c t o r
сбой Java Стандартные коллекции Java включают метод toString(), поэтому они могут генерировать свои собственные представления String, включая содержащиеся в них объекты. Например, в векторе toString() выполняет шаги и проходит через элементы вектора и вызывает toString() для каждого элемента. Предположим, теперь мы хотим распечатать адрес нашего класса. Кажется, что достаточно просто сослаться на это (особенно это делают программисты на C++):
public class CrashJava {
public String toString() {
return "CrashJava address: " + this + "\n";
}
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 10; i++)
v.addElement(new CrashJava());
System.out.println(v);
}
}
В этот момент происходит автоматическое преобразование типов строк. Когда мы используем следующее утверждение: "Адрес CrashJava: " + это Компилятор находит "+" и что-то еще, что не является строкой сразу после строки, поэтому он пытается преобразовать это в строку. Преобразование вызывается toString(), что делает рекурсивный вызов. Если это происходит внутри Vector, это выглядит так, как будто стек переполняется, и механизм контроля нарушений вообще не имеет возможности среагировать. Если вы действительно хотите распечатать адрес объекта в этом случае, решение состоит в том, чтобы вызвать метод Object toString. Вам не нужно добавлять это на данном этапе, просто используйте super.toString(). Конечно, есть также предварительное условие для использования этого подхода: мы должны наследоваться непосредственно от Object, иначе ни один из родительских классов не переопределит метод toString.
B i t S e t
BitSet на самом деле является вектором, состоящим из «двоичных битов». BitSet следует использовать, если вы хотите эффективно сохранять большое количество информации о включении и выключении. Это имеет смысл только с точки зрения размера; если желателен эффективный доступ, он будет медленнее, чем использование какого-либо массива с внутренней типизацией. Минимальная длина BitSet равна длине Long: 64 бита. Это означает, что если мы собираемся хранить меньшие данные, чем это, например, 8-битные данные, то BitSet будет расточительным. Поэтому лучше создать свой собственный класс и использовать его для хранения собственных флагов.
S t a c k
Стек также иногда называют коллекцией «последним пришел, первым ушел» (LIFO). Другими словами, последнее, что мы "положим" в стек, будет следующим. «Всплывающий». Как и во всех других коллекциях Java, мы извлекаем и извлекаем «объекты», поэтому нам нужно что-то делать с тем, что мы извлекаем. Делайте «форму». Ниже приведен простой пример стека, который считывает каждую строку массива и помещает ее в стек в виде строки.
public class Stacks {
static String[] months = { "January", "February", "March", "April", "May",
"June", "July", "August", "September", "October", "November",
"December" };
public static void main(String[] args) {
Stack stk = new Stack();
for (int i = 0; i < months.length; i++)
stk.push(months[i] + " ");
System.out.println("stk = " + stk);
// Treating a stack as a Vector:
stk.addElement("The last line");
System.out.println("element 5 = " + stk.elementAt(5));
System.out.println("popping elements:");
while (!stk.empty())
System.out.println(stk.pop());
}
}
Каждая строка массива месяцев наследуется с помощью функции push() в стек, а затем с помощью функции pop() она извлекается из вершины стека. Следует объявить, что векторные операции также могут выполняться над объектами стека. Это может определяться унаследованным признаком - стек "принадлежит" вектору. Следовательно, операции, которые можно выполнять с вектором, также можно выполнять и со стеком, например метод elementAt().
H a s h t a b l e
Вектор позволяет нам выбирать из ряда объектов с номером, поэтому он фактически связывает номер с объектом. Но что, если мы хотим выбрать ряд объектов на основе других критериев? Примером этого является стек: критерием его выбора является «последнее, что помещается в стек». Эту концепцию «выбора из набора объектов» также можно назвать «картой», «словарем» или «ассоциативным массивом». Концептуально это похоже на Вектор, но вместо того, чтобы находить объекты по номерам, они находят их по другому объекту! Обычно это относится к важному процессу в программе. В Java эта концепция воплощена в абстрактном классе Dictionary. Интерфейс этого класса очень интуитивно понятен. size() сообщает нам, сколько элементов содержится в нем; isEmpty() определяет, содержится ли элемент (true, если да); put(Object key, Object value) добавляет значение ( что мы хотим) и связать его с тем же ключом (то, что вы хотите использовать для его поиска); get(Object key) получает значение, соответствующее ключу; и remove(Object Key) используется для удаления "ключ-значение". "из списка" справа. Также можно использовать методы перечисления: keys() производит перечисление ключей, elements() производит перечисление всех значений. Это все, что есть в словаре.
public class AssocArray extends Dictionary {
private Vector keys = new Vector();
private Vector values = new Vector();
public int size() {
return keys.size();
}
public boolean isEmpty() {
return keys.isEmpty();
}
public Object put(Object key, Object value) {
keys.addElement(key);
values.addElement(value);
return key;
}
public Object get(Object key) {
int index = keys.indexOf(key);
// indexOf() Returns -1 if key not found:
if (index == -1)
return null;
return values.elementAt(index);
}
public Object remove(Object key) {
int index = keys.indexOf(key);
if (index == -1)
return null;
keys.removeElementAt(index);
Object returnval = values.elementAt(index);
values.removeElementAt(index);
return returnval;
}
public Enumeration keys() {
return keys.elements();
}
public Enumeration elements() {
return values.elements();
}
// Test it:
public static void main(String[] args) {
AssocArray aa = new AssocArray();
for (char c = 'a'; c <= 'z'; c++)
aa.put(String.valueOf(c), String.valueOf(c).toUpperCase());
char[] ca = { 'a', 'e', 'i', 'o', 'u' };
for (int i = 0; i < ca.length; i++)
System.out.println("Uppercase: " + aa.get(String.valueOf(ca[i])));
}
}
Первая проблема, которую мы заметили в определении AssocArray, заключается в том, что он «расширяет» словарь. Это означает, что AssocArray является типом Dictionary, поэтому его можно запрашивать так же, как Dictionary. Если вы хотите создать свой собственный словарь и сделать это прямо здесь, все, что вам нужно сделать, это заполнить все методы, расположенные внутри словаря (и вы должны переопределить все методы, потому что Они - кроме строителей - абстрактны). Стандартная библиотека Java содержит только один вариант словаря под названием Hashtable (хэш-таблица, примечание ③). Хеш-таблица Java имеет тот же интерфейс, что и AssocArray (поскольку обе они наследуются от Dictionary). Но есть один аспект, отражающий разницу: эффективность исполнения. Если вы подумаете о том, что вам нужно сделать для get(), вы увидите, что поиск ключей в векторе выполняется намного медленнее. Но в настоящее время использование хэш-таблицы может значительно ускорить работу. Вместо использования длинных методов линейного поиска для поиска ключа используйте специальное значение, называемое «хэш-код». Хэш-код берет информацию из объекта и преобразует ее в «относительно уникальное» целое число (int) для этого объекта. Все объекты имеют хеш-код, а hashCode() — это метод корневого класса Object. Hashtable берет hashCode() объекта и использует его для быстрого поиска ключа.
class Counter {
int i = 1;
public String toString() {
return Integer.toString(i);
}
}
class Statistics {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for (int i = 0; i < 10000; i++) {
// Produce a number between 0 and 20:
Integer r = new Integer((int) (Math.random() * 20));
if (ht.containsKey(r))
((Counter) ht.get(r)).i++;
else
ht.put(r, new Counter());
}
System.out.println(ht);
}
}
- Создайте «ключевой» класс Но при использовании хеш-таблицы, как только мы создадим свой собственный класс в качестве ключа, чтобы сделать использования, вы столкнетесь с очень распространенной проблемой. Например, предположим, что система прогноза погоды сопоставляет объекты Groundhog с Prediction. Это кажется довольно интуитивным: мы создаем два класса и используем Groundhog в качестве ключа и Prediction в качестве значения. Следующее:
class Groundhog {
int ghNumber;
Groundhog(int n) {
ghNumber = n;
}
}
class Prediction {
boolean shadow = Math.random() > 0.5;
public String toString() {
if (shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
}
public class SpringDetector {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for (int i = 0; i < 10; i++)
ht.put(new Groundhog(i), new Prediction());
System.out.println("ht = " + ht + "\n");
System.out.println("Looking up prediction for groundhog #3:");
Groundhog gh = new Groundhog(3);
if (ht.containsKey(gh))
System.out.println((Prediction) ht.get(gh));
}
}
Проблема в том, что Groundhog наследуется от общего корневого класса Object (если Если указан базовый класс, все классы в конечном итоге наследуются от Object). На самом деле хэш-код каждого объекта генерируется с использованием метода Object hashCode(), и по умолчанию используется только адрес его объекта. Таким образом, первый экземпляр Groundhog(3) не создает тот же хэш-код, что и второй экземпляр Groundhog(3), который мы используем для получения Вы можете подумать, что все, что вам нужно сделать на этом этапе, — это правильно переопределить hashCode(). Но это по-прежнему не работает, если вы не сделаете еще одну вещь: не переопределите функцию equals(), которая также является частью Object. Этот метод используется, когда хеш-таблица пытается определить, равен ли наш ключ ключу в таблице. Аналогично, Object.equals() по умолчанию просто сравнивает адреса объектов, так что Groundhog(3) не равно Еще один сурок(3). Следовательно, чтобы использовать свой собственный класс в качестве ключа в хэш-таблице, вы должны переопределить как hashCode(), так и equals(), как показано ниже:
class Groundhog {
int ghNumber;
Groundhog(int n) {
ghNumber = n;
}
}
class Prediction {
boolean shadow = Math.random() > 0.5;
public String toString() {
if (shadow)
return "Six more weeks of Winter!";
else
return "Early Spring!";
}
}
public class SpringDetector {
public static void main(String[] args) {
Hashtable ht = new Hashtable();
for (int i = 0; i < 10; i++)
ht.put(new Groundhog(i), new Prediction());
System.out.println("ht = " + ht + "\n");
System.out.println("Looking up prediction for groundhog #3:");
Groundhog gh = new Groundhog(3);
if (ht.containsKey(gh))
System.out.println((Prediction) ht.get(gh));
}
}
Groundhog2.hashCode() возвращает номер сурка в качестве идентификатора (в этом примере программист должен убедиться, что никакие два сурка не сосуществуют с одинаковым идентификационным номером). Чтобы вернуть уникальный идентификатор, hashCode() не требуется, метод equals() должен уметь строго определять, равны ли два объекта. Метод equals() выполняет две проверки: проверяет, является ли объект нулевым; если нет, продолжает проверять, является ли он экземпляром Groundhog2 (используйте ключевое слово instanceof). Даже для того, чтобы продолжить с equals(), это должен быть Groundhog2. Как видите, это сравнение основано на фактическом ghNumber. На этот раз, как только мы запустим программу, мы увидим, что она, наконец, выдает правильный вывод (многие классы библиотеки Java переопределяют методы hashcode() и equals(), чтобы приспособиться к тому, что они предоставляют).
Повторное посещение счетчиков
Отделите операции, проходящие через последовательность, от базовой структуры этой последовательности. В следующем примере класс PrintData перемещается по последовательности с помощью Enumeration и вызывает метод toString() для каждого объекта. На этом этапе создаются два разных типа коллекций: вектор и хеш-таблица. и заполнить их соответственно Для объектов Mouse и Hamster, поскольку Enumeration скрывает структуру базовой коллекции, PrintData не знает и не заботится о том, из какого типа коллекции происходит Enumeration:
class PrintData {
static void print(Enumeration e) {
while (e.hasMoreElements())
System.out.println(e.nextElement().toString());
}
}
class Enumerators2 {
public static void main(String[] args) {
Vector v = new Vector();
for (int i = 0; i < 5; i++)
v.addElement(new Mouse(i));
Hashtable h = new Hashtable();
for (int i = 0; i < 5; i++)
h.put(new Integer(i), new Hamster(i));
System.out.println("Vector");
PrintData.print(v.elements());
System.out.println("Hashtable");
PrintData.print(h.elements());
}
}
Обратите внимание, что PrintData.print() использует тот факт, что объекты в этих коллекциях относятся к классу Object, поэтому он вызывает toString(). но в При решении собственных практических задач вам часто приходится следить за тем, чтобы ваше Enumeration проходило через определенный тип коллекции. Например, может потребоваться коллекция Все элементы являются Shape (геометрической фигурой) и имеют метод draw(). Если это произойдет, вы должны Объект, возвращаемый Enumeration.nextElement(), преобразуется для создания формы.
Сортировать
Одна из проблем при написании универсального кода сортировки заключается в том, что сравнения должны выполняться на основе фактического типа объектов, чтобы добиться правильной сортировки. Конечно, один из способов — написать отдельный метод сортировки для каждого типа. Однако имейте в виду, что это затрудняет повторное использование кода при последующем добавлении новых типов.Основная цель программирования — «отделить то, что меняется, от того, что остается неизменным». Здесь код, который остается неизменным, — это общий алгоритм сортировки, а то, что меняется каждый раз при его использовании, — это фактический метод сравнения объектов. Поэтому вместо «жесткого кодирования» кода сравнения в несколько различных процедур сортировки мы используем метод «обратного вызова».При использовании обратных вызовов часть кода, которая часто изменяется, инкапсулируется в собственный класс, а код, который всегда остается одним и тем же, «обратно обращается» к изменяющемуся коду. Это позволяет различным объектам выражать разные сравнения, передавая им один и тот же код сортировки. Следующий «Интерфейс» показывает, как сравнивать два объекта, инкапсулируя те «вещи, которые нужно изменить»:
interface Compare {
boolean lessThan(Object lhs, Object rhs);
boolean lessThanOrEqual(Object lhs, Object rhs);
}
Для обоих методов левая сторона представляет «левый» объект в этом сравнении, а правая сторона представляет «правый» объект. Можно создать подкласс Vector для реализации «быстрой сортировки» через Compare. Для этого алгоритма, включая его скорость и принцип и т.д.
public class SortVector extends Vector {
private Compare compare; // To hold the callback
public SortVector(Compare comp) {
compare = comp;
}
public void sort() {
quickSort(0, size() - 1);
}
private void quickSort(int left, int right) {
if (right > left) {
Object o1 = elementAt(right);
int i = left - 1;
int j = right;
while (true) {
while (compare.lessThan(elementAt(++i), o1))
;
while (j > 0)
if (compare.lessThanOrEqual(elementAt(--j), o1))
break; // out of while
if (i >= j)
break;
swap(i, j);
}
swap(i, right);
quickSort(left, i - 1);
quickSort(i + 1, right);
}
}
private void swap(int loc1, int loc2) {
Object tmp = elementAt(loc1);
setElementAt(elementAt(loc2), loc1);
setElementAt(tmp, loc2);
}
}
Чтобы использовать SortVector, мы должны создать класс, реализующий Compare для объектов, которые мы хотим отсортировать. Внутренний класс в настоящее время не особо важен, но он полезен для организации кода. Вот пример для объектов String
public class StringSortTest {
static class StringCompare implements Compare {
public boolean lessThan(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) < 0;
}
public boolean lessThanOrEqual(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) <= 0;
}
}
public static void main(String[] args) {
SortVector sv = new SortVector(new StringCompare());
sv.addElement("d");
sv.addElement("A");
sv.addElement("C");
sv.addElement("c");
sv.addElement("b");
sv.addElement("B");
sv.addElement("D");
sv.addElement("a");
sv.sort();
Enumeration e = sv.elements();
while (e.hasMoreElements())
System.out.println(e.nextElement());
}
}
После того, как фреймворк настроен, очень легко повторно использовать такой дизайн — просто напишите класс, инкапсулируйте в нем то, что «необходимо изменить», и передайте объект в SortVector. Наследование ( extends ) используется здесь для создания нового типа вектора, то есть SortVector — это вектор с некоторыми дополнительными функциями. Наследование может сыграть здесь большую роль, но приносит проблемы. Это делает некоторые методы окончательными, поэтому их нельзя переопределить. Если вы хотите создать отсортированный вектор, который принимает и производит только объекты String, у вас возникнут проблемы. Потому что и addElement(), и elementAt() имеют атрибуты final, и все они являются методами, которые мы должны переопределить, иначе мы не сможем добиться только получения и создания объектов String. Но, с другой стороны, рассмотрим «композиционный» подход: поместите объект в новый класс. На этом этапе вместо того, чтобы переписывать приведенный выше код для достижения этой цели, просто используйте SortVector в новом классе. В этом случае внутренний класс, реализующий интерфейс Compare, может быть создан «анонимно».
import java.util.*;
public class StrSortVector {
private SortVector v = new SortVector(
// Anonymous inner class:
new Compare() {
public boolean lessThan(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) < 0;
}
public boolean lessThanOrEqual(Object l, Object r) {
return ((String) l).toLowerCase().compareTo(
((String) r).toLowerCase()) <= 0;
}
});
private boolean sorted = false;
public void addElement(String s) {
v.addElement(s);
sorted = false;
}
public String elementAt(int index) {
if(!sorted) {
v.sort();232
sorted = true;
}
return (String)v.elementAt(index);
}
public Enumeration elements() {
if (!sorted) {
v.sort();
sorted = true;
}
return v.elements();
}
// Test it:
public static void main(String[] args) {
StrSortVector sv = new StrSortVector();
sv.addElement("d");
sv.addElement("A");
sv.addElement("C");
sv.addElement("c");
sv.addElement("b");
sv.addElement("B");
sv.addElement("D");
sv.addElement("a");
Enumeration e = sv.elements();
while (e.hasMoreElements())
System.out.println(e.nextElement());
}
}
новая коллекция
这张图刚开始的时候可能让人有点儿摸不着头脑,相信大家会真正理解它实际只有三个集合组件: Map, List 和 Set。而且每个组件实际只有两、三种实现方式 虚线框代表“接口”,点线框代表“抽象”类,而实线框代表普通(实际)类。点线箭头表示一个特定的类准备实现一个接口(在抽象类的情况下,则是“部分”实现一个接口)。双线箭头表示一个类可生成箭头指向的那个类的对象。 致力于容纳对象的接口是 Collection, List, Set 和 Map。在传统情况下,我们需要写大量代码才能同这些接口打交道。而且为了指定自己想使用的准确类型,必须在创建之初进行设置。所以可能创建下面这样的一 Список:List x = new LinkedList();
Конечно, вы также можете использовать x в качестве LinkedList (вместо обычного списка) и использовать x для переноса точной информации о типе. Преимущество использования интерфейса заключается в том, что, как только вы решите изменить детали своей реализации, все, что вам нужно сделать, это изменить ее во время создания, например:
List x = new ArrayList();
В иерархии классов вы можете увидеть большое количество классов, начинающихся с «Abstract», что поначалу может сбивать с толку. На самом деле это инструменты, которые «частично» реализуют определенный интерфейс. Например, если вы хотите сгенерировать свой собственный набор, вы не начинаете с интерфейса набора и не реализуете все методы самостоятельно. Вместо этого мы можем наследовать от AbstractSet и получить собственный новый класс с минимальными усилиями. Тем не менее, новая библиотека коллекций содержит достаточно функционала практически для всех наших нужд. Поэтому для наших целей мы можем игнорировать все классы, начинающиеся с «Abstract». Итак, глядя на эту диаграмму, все, о чем вам действительно нужно заботиться, это «интерфейс» вверху и нормальный (фактический) класс — оба окружены сплошными прямоугольниками. Обычно необходимо сгенерировать объект фактического класса и преобразовать его в соответствующий интерфейс. Затем вы можете использовать этот интерфейс в любом месте вашего кода. Вот простой пример, который заполняет коллекцию объектами String, а затем выводит каждый элемент в коллекции:
public class SimpleCollection {
public static void main(String[] args) {
Collection c = new ArrayList();
for (int i = 0; i < 10; i++)
c.add(Integer.toString(i));
Iterator it = c.iterator();
while (it.hasNext())
System.out.println(it.next());
}
}
Первая строка main() создает объект ArrayList, а затем преобразует его в коллекцию. Поскольку в этом примере используется только метод Collection, любой объект класса, наследуемый от Collection, будет работать нормально. Но ArrayList — это типичный Collection, который заменяет Vector. Роль метода add() заключается в добавлении нового элемента в коллекцию. Однако в пользовательской документации осторожно указано, что add() «гарантирует, что эта коллекция содержит указанный элемент». Это закладывает основу для Set, который на самом деле добавит элемент, только если он не существует. Для ArrayList и любой другой формы List add() определенно означает «добавлять напрямую». Используя метод iterator(), все коллекции могут генерировать «Итератор». Повторитель на самом деле похож на «перечисление», замену последнего, просто:(1)Он принимает исторически принятое по умолчанию и широко распространенное имя в ООП (итератор).(2)Используются более короткие имена, чем Enumeration: hasNext() заменяет hasMoreElement(), а next() заменяет nextElement().(3)Добавлен новый метод remove(), который удаляет предыдущий элемент, созданный Iterator. Поэтому каждый раз, когда вы вызываете next(), просто вызовите remove() один раз
ИСПОЛЬЗОВАТЬ КОЛЛЕКЦИИ
В следующей таблице приведены все, что вы можете сделать с набором (то же самое можно сделать с наборами и списками, хотя список также предоставляет некоторые дополнительные функции). Карта не наследуется от коллекции, поэтому обрабатывается отдельно.
boolean add(Object) * Гарантирует, что набор содержит аргумент. Возвращает false, если не добавляет аргумент boolean addAll(Collection) *Добавляет все элементы в пределах аргумента. Возвращает true, если элемент не был добавлен void clear() * удалить все элементы коллекции boolean contains(Object) возвращает true, если коллекция содержит аргумент логическое значение containsAll(Collection) возвращает true, если коллекция содержит все элементы в аргументе boolean isEmpty() возвращает true, если в коллекции нет элементов Итератор iterator() возвращает итератор для перебора элементов коллекции. boolean remove(Object) *Если аргумент находится в наборе, удалить экземпляр этого элемента. Возвращается при удалении "настоящий" boolean removeAll(Collection) *Удалить все элементы в аргументе. Возвращает true, если были сделаны какие-либо удаления boolean continueAll(Collection) *Сохраняет только элементы, содержащиеся в аргументе (теоретическое "пересечение"). Если введено Возвращает «true», если внесены какие-либо изменения int size() возвращает количество элементов в коллекции Object[] toArray() возвращает массив, содержащий все элементы коллекции * Это необязательный метод, и некоторые коллекции могут не реализовывать его. Если это так, метод встречает UnsupportedOperatiionException, то есть исключение «операция не поддерживается».
В следующем примере показаны все методы. Точно так же они действительны только для вещей, унаследованных от коллекций, ArrayList используется как своего рода «необычный знаменатель».
public class Collection1 {
// Fill with 'size' elements, start
// counting at 'start':
public static Collection fill(Collection c, int start, int size) {
for (int i = start; i < start + size; i++)
c.add(Integer.toString(i));
return c;
}
// Default to a "start" of 0:
public static Collection fill(Collection c, int size) {
return fill(c, 0, size);
}
// Default to 10 elements:
public static Collection fill(Collection c) {
return fill(c, 0, 10);
}
// Create & upcast to Collection:
public static Collection newCollection() {
return fill(new ArrayList());
// ArrayList is used for simplicity, but it's
// only seen as a generic Collection
// everywhere else in the program.
}
// Fill a Collection with a range of values:
public static Collection newCollection(int start, int size) {
return fill(new ArrayList(), start, size);
}
// Moving through a List with an iterator:
public static void print(Collection c) {
for (Iterator x = c.iterator(); x.hasNext();)
System.out.print(x.next() + " ");
System.out.println();
}
public static void main(String[] args) {
Collection c = newCollection();
c.add("ten");
c.add("eleven");
print(c);
// Make an array from the List:
Object[] array = c.toArray();
// Make a String array from the List:
String[] str = (String[]) c.toArray(new String[1]);
// Find max and min elements; this means
// different things depending on the way
// the Comparable interface is implemented:
System.out.println("Collections.max(c) = " + Collections.max(c));
System.out.println("Collections.min(c) = " + Collections.min(c));
// Add a Collection to another Collection
c.addAll(newCollection());
print(c);
c.remove("3"); // Removes the first one
print(c);
c.remove("3"); // Removes the second one
print(c);
// Remove all components that are in the
// argument collection:
c.removeAll(newCollection());
print(c);
c.addAll(newCollection());
print(c);
// Is an element in this Collection?
System.out.println("c.contains(\"4\") = " + c.contains("4"));
// Is a Collection in this Collection?
System.out.println("c.containsAll(newCollection()) = "
+ c.containsAll(newCollection()));
Collection c2 = newCollection(5, 3);
// Keep all the elements that are in both
// c and c2 (an intersection of sets):
c.retainAll(c2);
print(c);
// Throw away all the elements in c that
// also appear in c2:
c.removeAll(c2);
System.out.println("c.isEmpty() = " + c.isEmpty());
c = newCollection();
print(c);
c.clear(); // Remove all elements
System.out.println("after c.clear():");
print(c);
}
}
Обе версии newCollection() создают ArrayList, содержащие разные наборы данных, и возвращают их как объекты коллекции. Так что, очевидно, ничего не будет использоваться, кроме интерфейса Collection.
Используйте списки
Порядок списка (интерфейса) — самая важная особенность списка, он гарантирует, что элементы находятся в указанном порядке.List добавляет несколько методов в Collection, чтобы мы могли вставлять и удалять элементы из середины списка (это рекомендуется только для LinkedList). List также генерирует ListIterator (итератор списка), который можно использовать для перемещения по списку в обоих направлениях, вставляя и удаляя элементы, расположенные в середине списка (опять же, это рекомендуется только для LinkedList).ArrayList Список, полученный путем возврата массива. Используется как контейнер объектов общего назначения для замены оригинального Vector. Позволяет нам быстро получать доступ к элементам, но немного замедляет вставку и удаление элементов из середины списка. В общем, вы должны использовать ListIterator только для обхода ArrayList вперед и назад, не используйте его для удаления и вставки элементов, по сравнению с LinkedList его эффективность намного ниже LinkedList обеспечивает оптимизированную производительность последовательного доступа, и в то же время он можно эффективно использовать в списке.Операции вставки и удаления выполняются в середине. Однако при произвольном доступе скорость довольно низкая, и вместо этого следует использовать ArrayList.также обеспечиваетaddFirst(), addLast(), getFirst(),getLast(), removeFirst() 以及 removeLast()
(не определено ни в одном интерфейсе или базовом классе) для использования в качестве спецификации, очереди и двунаправленной очереди
public class List1 {
// Wrap Collection1.fill() for convenience:
public static List fill(List a) {
return (List) Collection1.fill(a);
}
// You can use an Iterator, just as with a
// Collection, but you can also use random
// access with get():
public static void print(List a) {
for (int i = 0; i < a.size(); i++)
System.out.print(a.get(i) + " ");
System.out.println();
}
static boolean b;
static Object o;
static int i;
static Iterator it;
static ListIterator lit;
public static void basicTest(List a) {
a.add(1, "x"); // Add at location 1
a.add("x"); // Add at end
// Add a collection:
a.addAll(fill(new ArrayList()));
// Add a collection starting at location 3:
a.addAll(3, fill(new ArrayList()));
b = a.contains("1"); // Is it in there?
// Is the entire collection in there?
b = a.containsAll(fill(new ArrayList()));
// Lists allow random access, which is cheap
// for ArrayList, expensive for LinkedList:
o = a.get(1); // Get object at location 1
i = a.indexOf("1"); // Tell index of object
// indexOf, starting search at location 2:
i = a.indexOf("1", 2);
b = a.isEmpty(); // Any elements inside?
it = a.iterator(); // Ordinary Iterator
lit = a.listIterator(); // ListIterator
lit = a.listIterator(3); // Start at loc 3
i = a.lastIndexOf("1"); // Last match
i = a.lastIndexOf("1", 2); // ...after loc 2
a.remove(1); // Remove location 1
a.remove("3"); // Remove this object
a.set(1, "y"); // Set location 1 to "y"
// Keep everything that's in the argument
// (the intersection of the two sets):
a.retainAll(fill(new ArrayList()));
// Remove elements in this range:
a.removeRange(0, 2);
// Remove everything that's in the argument:
a.removeAll(fill(new ArrayList()));
i = a.size(); // How big is it?
a.clear(); // Remove all elements
}
public static void iterMotion(List a) {
ListIterator it = a.listIterator();
b = it.hasNext();
b = it.hasPrevious();
o = it.next();
i = it.nextIndex();
o = it.previous();
i = it.previousIndex();
}
public static void iterManipulation(List a) {
ListIterator it = a.listIterator();
it.add("47");
// Must move to an element after add():
it.next();
// Remove the element that was just produced:
it.remove();
// Must move to an element after remove():
it.next();
// Change the element that was just produced:
it.set("47");
}
public static void testVisual(List a) {
print(a);
List b = new ArrayList();
fill(b);
System.out.print("b = ");
print(b);
a.addAll(b);
a.addAll(fill(new ArrayList()));
print(a);
// Shrink the list by removing all the
// elements beyond the first 1/2 of the list
System.out.println(a.size());
System.out.println(a.size() / 2);
a.removeRange(a.size() / 2, a.size() / 2 + 2);
print(a);
// Insert, remove, and replace elements
// using a ListIterator:
ListIterator x = a.listIterator(a.size() / 2);
x.add("one");
print(a);
System.out.println(x.next());
x.remove();
System.out.println(x.next());
x.set("47");
print(a);
// Traverse the list backwards:
x = a.listIterator(a.size());
while (x.hasPrevious())
System.out.print(x.previous() + " ");
System.out.println();
System.out.println("testVisual finished");
}
// There are some things that only
// LinkedLists can do:
public static void testLinkedList() {
LinkedList ll = new LinkedList();
Collection1.fill(ll, 5);
print(ll);
// Treat it like a stack, pushing:
ll.addFirst("one");
ll.addFirst("two");
print(ll);
// Like "peeking" at the top of a stack:
System.out.println(ll.getFirst());
// Like popping a stack:
System.out.println(ll.removeFirst());
System.out.println(ll.removeFirst());
// Treat it like a queue, pulling elements
// off the tail end:
System.out.println(ll.removeLast());
// With the above operations, it's a dequeue!
print(ll);
}
public static void main(String args[]) {
// Make and fill a new list each time:
basicTest(fill(new LinkedList()));
basicTest(fill(new ArrayList()));
iterMotion(fill(new LinkedList()));
iterMotion(fill(new ArrayList()));
iterManipulation(fill(new LinkedList()));
iterManipulation(fill(new ArrayList()));
testVisual(fill(new LinkedList()));
testLinkedList();
}
}
В basicTest() и iterMotiion() вызовы просто выполняются, чтобы выявить правильный синтаксис. И несмотря на захват возврата значение, но не используется. В некоторых случаях возвращаемые значения не захватываются, потому что они не особенно полезны. в официальном использовании Перед ними вы должны внимательно изучить свою онлайн-документацию, чтобы освоить полное и правильное использование этих методов.
Пример использования ArrayList
import java.awt.List;
import java.util.ArrayList;
import java.util.Iterator;
/**
* @author sihai
* @time 2018/4/19
* ArrayList用法示例说明
*
*/
public class Main {
public static void main(String[] args) {
//ArrayList用法示例
ArrayList<String> m_ArrayList=new ArrayList<String>();
m_ArrayList.add("Evankaka");
m_ArrayList.add("sihai");
m_ArrayList.add("德德");
m_ArrayList.add("Evankaka");
m_ArrayList.add("小红");
m_ArrayList.set(2,"sihai2");// 将索引位置为2的对象修改
m_ArrayList.add(3,"好好学java");// 将对象添加到索引位置为3的位置
//ArrayList遍历方法1
Iterator<String> it_ArrayList = m_ArrayList.iterator();
System.out.println("ArrayList遍历方法1");
while (it_ArrayList.hasNext()) {
System.out.println(it_ArrayList.next());
}
//ArrayList遍历方法2
System.out.println("ArrayList遍历方法2");
for(Object o:m_ArrayList){
System.out.println(o);
}
//ArrayList遍历方法2
System.out.println("ArrayList遍历方法3");
for(int i = 0; i<m_ArrayList.size(); i++){
System.out.println(m_ArrayList.get(i));
}
//删除元素
m_ArrayList.remove("Evankaka");
it_ArrayList = m_ArrayList.iterator();
System.out.println("ArrayList删除元素后的遍历");
while (it_ArrayList.hasNext()) {
String m_String=it_ArrayList.next();
if(m_String.equals("好好学java")){
it_ArrayList.remove();
}else{
System.out.println(m_String);
}
}
}
}
Выходной результат:
Метод обхода ArrayList 1 Эванкака Сихай сихай2 выучить java Эванкака немного красный Метод обхода ArrayList 2 Эванкака Сихай сихай2 выучить java Эванкака немного красный Метод обхода ArrayList 3 Эванкака Сихай сихай2 выучить java Эванкака немного красный Обход ArrayList после удаления элементов Сихай сихай2 Эванкака немного красный
Примечание
(1) В процессе использования Iterator для перебора коллекции элементы коллекции нельзя модифицировать, иначе будет выдано исключение. и Iterator может выполнять итерацию только в обратном направлении (2) Если вы хотите удалить элемент во время цикла, вы можете вызвать только метод it.remove, а не метод list.remove, иначе возникнет ошибка параллельного доступа.
Использовать наборы
Набор — это точно Коллекция, только с другим поведением (это идеальное применение экземпляров и полиморфизма: для выражения разных поведений). это здесь,Набор допускает только один экземпляр каждого объекта (как вы увидите позже, состав «значения» объекта довольно сложен) Набор (интерфейс) Каждый элемент, добавляемый в Набор, должен быть уникальным, иначе Набор не будет добавлять повторяющиеся элементы. Объекты, добавляемые в набор, должны определять метод equals(), чтобы установить уникальность объекта. Set имеет тот же интерфейс, что и Collection . Набор не гарантирует, что он будет поддерживать свои элементы в любом конкретном порядке. HashSetИспользуется для всех наборов, кроме очень маленьких. Объекты также должны определять hashCode()ArraySetНабор, вытесненный из массива. Предназначен для очень маленьких наборов, особенно для тех, которые необходимо часто создавать и удалять. для маленьких Set, по сравнению с HashSet, ArraySet намного дешевле в создании и итерации. Но по мере того, как набор растет, растет и его производительность. будут сильно снижены. HashCode() не требуетсяTreeSetПоследовательный набор (примечание ⑦), оттесненный «красно-черным деревом». Таким образом, мы можем ссылаться на множество из множества Последовательный сбор
public class Set1 {
public static void testVisual(Set a) {
Collection1.fill(a);
Collection1.fill(a);
Collection1.fill(a);
Collection1.print(a); // No duplicates!
// Add another set to this one:
a.addAll(a);
a.add("one");
a.add("one");
a.add("one");
Collection1.print(a);
// Look something up:
System.out.println("a.contains(\"one\"): " + a.contains("one"));
}
public static void main(String[] args) {
testVisual(new HashSet());
testVisual(new TreeSet());
}
}
В Set добавляются повторяющиеся значения, но при печати мы видим, что Set принимает только один экземпляр каждого значения.Когда вы запустите эту программу, вы заметите, что порядок, поддерживаемый HashSet, отличается от ArraySet. Это связано с их разными методами сохранения элементов для их последующего позиционирования. Наборы ArraySet сохраняют свое последовательное состояние, в то время как наборы HashSet используют хеш-функцию, которая специально разработана для быстрого поиска).
class MyType implements Comparable {
private int i;
public MyType(int n) {
i = n;
}
public boolean equals(Object o) {
return (o instanceof MyType) && (i == ((MyType) o).i);
}
public int hashCode() {
return i;
}
public String toString() {
return i + " ";
}
public int compareTo(Object o) {
int i2 = ((MyType) o).i;
return (i2 < i ? -1 : (i2 == i ? 0 : 1));
}
}
public class Set2 {
public static Set fill(Set a, int size) {
for (int i = 0; i < size; i++)
a.add(new MyType(i));
return a;
}
public static Set fill(Set a) {
return fill(a, 10);
}
public static void test(Set a) {
fill(a);
fill(a); // Try to add duplicates
fill(a);
a.addAll(fill(new TreeSet()));
System.out.println(a);
}
public static void main(String[] args) {
test(new HashSet());
test(new TreeSet());
}
}
Но использование hashCode() необходимо только в том случае, если класс должен быть помещен в HashSet, что вполне возможно, поскольку сначала следует выбирать реализацию в виде набора.
Используйте Карты
Карта (интерфейс) поддерживает соответствие "ключ-значение" (пару) для поиска соответствующего значения по ключуHashMapРеализовано на основе хэш-таблицы (используйте ее вместо Hashtable). Эта форма имеет наиболее стабильную производительность для вставки и извлечения пар ключ-значение. Эту функцию можно настроить через конструктор, чтобы установить «емкость» и «коэффициент загрузки» хеш-таблицы. ArrayMap — это Map, оттесненный ArrayList. Обеспечивает точный контроль над повторяющимися последовательностями. Предназначен для очень маленьких карт, особенно для тех, которые необходимо часто создавать и удалять. Для очень маленьких Карт создание и итерация обходятся дороже, чем HashMap намного ниже. Однако после того, как карта станет больше, производительность соответственно сильно снизится. TreeMap реализован на основе «красно-черного» дерева. При просмотре ключей или пар ключ-значение они располагаются в фиксированном порядке (в зависимости от Comparable или Comparator, об этом позже). Самым большим преимуществом TreeMap является то, что мы получаем отсортированные результаты. TreeMap — это единственный тип Map, у которого есть метод subMap(), который можно использовать для возврата части дерева.
public class Map1 {
public final static String[][] testData1 = {
{ "Happy", "Cheerful disposition" },
{ "Sleepy", "Prefers dark, quiet places" },
{ "Grumpy", "Needs to work on attitude" },
{ "Doc", "Fantasizes about advanced degree" },
{ "Dopey", "'A' for effort" },
{ "Sneezy", "Struggles with allergies" },
{ "Bashful", "Needs self-esteem workshop" }, };
public final static String[][] testData2 = {
{ "Belligerent", "Disruptive influence" },
{ "Lazy", "Motivational problems" },
{ "Comatose", "Excellent behavior" } };
public static Map fill(Map m, Object[][] o) {
for (int i = 0; i < o.length; i++)
m.put(o[i][0], o[i][1]);
return m;
}
// Producing a Set of the keys:
public static void printKeys(Map m) {
System.out.print("Size = " + m.size() + ", ");
System.out.print("Keys: ");
Collection1.print(m.keySet());
}
// Producing a Collection of the values:
public static void printValues(Map m) {
System.out.print("Values: ");
Collection1.print(m.values());
}
// Iterating through Map.Entry objects (pairs):
public static void print(Map m) {
Collection entries = m.entries();
Iterator it = entries.iterator();
while (it.hasNext()) {
Map.Entry e = (Map.Entry) it.next();
System.out.println("Key = " + e.getKey() + ", Value = "
+ e.getValue());
}
}
public static void test(Map m) {
fill(m, testData1);
// Map has 'Set' behavior for keys:
fill(m, testData1);
printKeys(m);
printValues(m);
print(m);
String key = testData1[4][0];
String value = testData1[4][1];
System.out.println("m.containsKey(\"" + key + "\"): "
+ m.containsKey(key));
System.out.println("m.get(\"" + key + "\"): " + m.get(key));
System.out.println("m.containsValue(\"" + value + "\"): "
+ m.containsValue(value));
Map m2 = fill(new TreeMap(), testData2);
m.putAll(m2);
printKeys(m);
m.remove(testData2[0][0]);
printKeys(m);
m.clear();
System.out.println("m.isEmpty(): " + m.isEmpty());
fill(m, testData1);
// Operations on the Set change the Map:
m.keySet().removeAll(m.keySet());
System.out.println("m.isEmpty(): " + m.isEmpty());
}
public static void main(String args[]) {
System.out.println("Testing HashMap");
test(new HashMap());
System.out.println("Testing TreeMap");
test(new TreeMap());
}
}
перебирать экземпляры карты
package com.test;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String, String> map = new HashMap<String, String>();
map.put("first", "linlin");
map.put("second", "好好学java");
map.put("third", "sihai");
map.put("first", "sihai2");
// 第一种:通过Map.keySet遍历key和value
System.out.println("===================通过Map.keySet遍历key和value:===================");
for (String key : map.keySet()) {
System.out.println("key= " + key + " and value= " + map.get(key));
}
// 第二种:通过Map.entrySet使用iterator遍历key和value
System.out.println("===================通过Map.entrySet使用iterator遍历key和value:===================");
Iterator<Map.Entry<String, String>> it = map.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> entry = it.next();
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第三种:通过Map.entrySet遍历key和value
System.out.println("===================通过Map.entrySet遍历key和value:===================");
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " and value= "
+ entry.getValue());
}
// 第四种:通过Map.values()遍历所有的value,但是不能遍历键key
System.out.println("===================通过Map.values()遍历所有的value:===================");
for (String v : map.values()) {
System.out.println("value= " + v);
}
}
}
Результат выглядит следующим образом:
==================== Перейдите ключ и значение через Map.keySet: =================== ключ = третий и значение = sihai ключ = первый и значение = sihai2 ключ = второй и значение = хорошо изучить Java ==================== Прохождение ключа и значения через Map.entrySet с помощью итератора: ==================== ключ = третий и значение = sihai ключ = первый и значение = sihai2 ключ = второй и значение = хорошо изучить Java ==================== Перейдите ключ и значение через Map.entrySet: =================== ключ = третий и значение = sihai ключ = первый и значение = sihai2 ключ = второй и значение = хорошо изучить Java ==================== Пройти все значения через Map.values(): ================== знак равно значение = сихай значение=сихай2 value= хорошо изучай Java
Решите, какую коллекцию использовать
Все ArrayList, LinkedList и Vector (примерно эквивалентные ArrayList) реализуют интерфейс List, поэтому наша программа будет получать одинаковые результаты независимо от того, какой из них мы выберем. Однако ArrayList (и Vector) поддерживаются массивом; LinkedList реализован в соответствии с подходом к обычному двусвязному списку, поскольку каждый отдельный объект содержит данные и дескрипторы предшествующего и последующего элементов в списке. По этой причине, если вы хотите выполнять много вставок и удалений в середине списка, то LinkedList, несомненно, является наиболее подходящим выбором (у LinkedList есть некоторые дополнительные функции, построенные на AbstractSequentialList). Если нет, предпочтите ArrayList, который может быть быстрее. В качестве другого примера, Set может быть реализован либо как ArraySet, либо как HashSet. ArraySet состоит из списка ArrayList. Серверная часть предназначена для поддержки только небольшого количества элементов, что особенно подходит для случаев, требующих создания и удаления большого количества объектов Set. Однако, как только вам нужно будет хранить большое количество элементов в вашем наборе, производительность ArraySet значительно снизится. При написании программы, для которой требуются наборы, HashSet должен быть выбран по умолчанию. И только в некоторых особых случаях (где есть острая необходимость в повышении производительности) следует переходить на ArraySet.
1. Решите, какой список использовать
Самый простой способ почувствовать разницу между различными реализациями List — пройти тест производительности.
public class ListPerformance {
private static final int REPS = 100;
private abstract static class Tester {
String name;
int size; // Test quantity
Tester(String name, int size) {
this.name = name;
this.size = size;
}
abstract void test(List a);
}
private static Tester[] tests = { new Tester("get", 300) {
void test(List a) {
for (int i = 0; i < REPS; i++) {
for (int j = 0; j < a.size(); j++)
a.get(j);
}
}
}, new Tester("iteration", 300) {
void test(List a) {
for (int i = 0; i < REPS; i++) {
Iterator it = a.iterator();
while (it.hasNext())
it.next();
}
}
}, new Tester("insert", 1000) {
void test(List a) {
int half = a.size() / 2;
String s = "test";
ListIterator it = a.listIterator(half);
for (int i = 0; i < size * 10; i++)
it.add(s);
}
}, new Tester("remove", 5000) {
void test(List a) {
ListIterator it = a.listIterator(3);
while (it.hasNext()) {
it.next();
it.remove();
}
}
}, };
public static void test(List a) {
// A trick to print out the class name:
System.out.println("Testing " + a.getClass().getName());
for (int i = 0; i < tests.length; i++) {
Collection1.fill(a, tests[i].size);
System.out.print(tests[i].name);
long t1 = System.currentTimeMillis();
tests[i].test(a);
long t2 = System.currentTimeMillis();
System.out.println(": " + (t2 - t1));
}
}
public static void main(String[] args) {
test(new ArrayList());
test(new LinkedList());
}
}
Внутренний класс Tester — это абстрактный класс, предоставляющий базовый класс для определенного теста. Он содержит строку для печати при запуске теста, параметр размера для подсчета количества тестов или элементов, построитель для инициализации полей и абстрактный метод test(). test() выполняет наиболее практическую тестовую работу. Все типы тестов собраны в одном месте: в массиве тестов. Мы инициализируем массив другим анонимным внутренним классом, наследуемым от Tester. Чтобы добавить или удалить тестовый элемент, просто добавьте или удалите определение внутреннего класса из массива, и все остальное произойдет автоматически.
Type Get Iteration Insert Remove
A r r a y L i s t 110 490 3790 8730
LinkedList 1980 220 110 110
Произвольный доступ (т. е. get() ) и циклы являются наиболее экономичными в ArrayList, но для LinkedList это требует больших накладных расходов. Но, с другой стороны, операции вставки и удаления в середине списка гораздо более рентабельны для LinkedList, чем для ArrayList.Возможно, лучше всего будет выбрать ArrayList в качестве отправной точки по умолчанию. Если вы обнаружите, что производительность снижается из-за большого количества вставок и удалений в будущем, еще не поздно подумать о переходе на LinkedList.
2. Решите, какой набор использовать
Вы можете выбирать между ArraySet и HashSet, в зависимости от размера набора (если вам нужен упорядоченный список из набора, используйте TreeSet;)
public class SetPerformance {
private static final int REPS = 200;
private abstract static class Tester {
String name;
Tester(String name) {
this.name = name;
}
abstract void test(Set s, int size);
}
private static Tester[] tests = { new Tester("add") {
void test(Set s, int size) {
for (int i = 0; i < REPS; i++) {
s.clear();
Collection1.fill(s, size);
}
}
}, new Tester("contains") {
void test(Set s, int size) {
for (int i = 0; i < REPS; i++)
for (int j = 0; j < size; j++)
s.contains(Integer.toString(j));
}
}, new Tester("iteration") {
void test(Set s, int size) {
for (int i = 0; i < REPS * 10; i++) {
Iterator it = s.iterator();
while (it.hasNext())
it.next();
}
}
}, };
public static void test(Set s, int size) {
// A trick to print out the class name:
System.out.println("Testing " + s.getClass().getName() + " size "
+ size);
Collection1.fill(s, size);
for (int i = 0; i < tests.length; i++) {
System.out.print(tests[i].name);
long t1 = System.currentTimeMillis();
tests[i].test(s, size);
long t2 = System.currentTimeMillis();
System.out.println(": " + ((double) (t2 - t1) / (double) size));
}
}
public static void main(String[] args) {
// Small:
test(new TreeSet(), 10);
test(new HashSet(), 10);
// Medium:
test(new TreeSet(), 100);
test(new HashSet(), 100);
// Large:
test(new HashSet(), 1000);
test(new TreeSet(), 1000);
}
}
Очевидно, что HashSet намного лучше, чем ArraySet, когда дело доходит до операций add() и contains(), и производительность, очевидно, не связана с количеством элементов. При написании программ вам почти никогда не нужно использовать ArraySet.
3. Решите, какую карту использовать
При выборе различных реализаций карты обратите внимание, что размер карты оказывает наибольшее влияние на производительность, как ясно иллюстрирует следующая тестовая программа. маленький:
public class MapPerformance {
private static final int REPS = 200;
public static Map fill(Map m, int size) {
for (int i = 0; i < size; i++) {
String x = Integer.toString(i);
m.put(x, x);
}
return m;
}
private abstract static class Tester {
String name;
Tester(String name) {
this.name = name;
}
abstract void test(Map m, int size);
}
private static Tester[] tests = { new Tester("put") {
void test(Map m, int size) {
for (int i = 0; i < REPS; i++) {
m.clear();
fill(m, size);
}
}
}, new Tester("get") {
void test(Map m, int size) {
for (int i = 0; i < REPS; i++)
for (int j = 0; j < size; j++)
m.get(Integer.toString(j));
}
}, new Tester("iteration") {
void test(Map m, int size) {
for (int i = 0; i < REPS * 10; i++) {
Iterator it = m.entries().iterator();
while (it.hasNext())
it.next();
}
}
}, };
public static void test(Map m, int size) {
// A trick to print out the class name:
System.out.println("Testing " + m.getClass().getName() + " size "
+ size);
fill(m, size);
for (int i = 0; i < tests.length; i++) {
System.out.print(tests[i].name);
long t1 = System.currentTimeMillis();
tests[i].test(m, size);
long t2 = System.currentTimeMillis();
System.out.println(": " + ((double) (t2 - t1) / (double) size));
}
}
public static void main(String[] args) {
// Small:
test(new Hashtable(), 10);
test(new HashMap(), 10);
test(new TreeMap(), 10);
// Medium:
test(new Hashtable(), 100);
test(new HashMap(), 100);
test(new TreeMap(), 100);
// Large:
test(new HashMap(), 1000);
test(new Hashtable(), 1000);
test(new TreeMap(), 1000);
}
}
Так как размер карты является самой серьезной проблемой, тайминг-тест программы делит время на размер (или вместимость) карты, чтобы получить впечатляющее Убедительные результаты испытаний. Диапазон результатов приведен ниже (на вашем компьютере они могут отличаться): Даже при размере 10 ArrayMap работает хуже, чем HashMap, за исключением повторяющихся циклов. А при работе с Картами итеративное действие обычно не имеет значения (обычно get() — это то, на что мы тратим больше всего времени). TreeMap обеспечивает превосходное время выполнения put() и итерации, но производительность get() оставляет желать лучшего. Но почему нам все еще нужно использовать TreeMap? Таким образом, мы можем использовать его не как карту, а как способ создания последовательного списка. **После заполнения TreeMap можно вызвать keySet(), чтобы получить Set «представление» ключей. Затем используйте toArray() для создания массива, содержащего эти ключи. Затем можно использовать статический метод Array.binarySearch() для быстрого поиска содержимого отсортированного массива. ** Конечно, это может быть необходимо только в том случае, если поведение HashMap неприемлемо. Потому что HashMap предназначен для выполнения операций быстрого поиска. Наконец,Когда мы используем Map, первым выбором должен быть HashMap. Лишь в редких случаях необходимо рассматривать другие методы
public class MapCreation {
public static void main(String[] args) {
final long REPS = 100000;
long t1 = System.currentTimeMillis();
System.out.print("Hashtable");
for (long i = 0; i < REPS; i++)
new Hashtable();
long t2 = System.currentTimeMillis();
System.out.println(": " + (t2 - t1));
t1 = System.currentTimeMillis();
System.out.print("TreeMap");
for (long i = 0; i < REPS; i++)
new TreeMap();
t2 = System.currentTimeMillis();
System.out.println(": " + (t2 - t1));
t1 = System.currentTimeMillis();
System.out.print("HashMap");
for (long i = 0; i < REPS; i++)
new HashMap();
t2 = System.currentTimeMillis();
System.out.println(": " + (t2 - t1));
}
}
TreeMap создается значительно быстрее, чем два других типа (но вы должны попробовать его сами, так как говорят, что новая версия потенциально повышает производительность ArrayMap). По этой причине и благодаря отличной производительности put() вышеупомянутого TreeMap, такого как Если вам нужно создать много карт, а позже нужно будет задействовать множество операций поиска, то лучшая стратегия:Создайте и заполните TreeMap; позже, когда объем поиска увеличится, преобразуйте важный TreeMap в HashMap — используйте построитель HashMap(Map).
неподдерживаемая операция
Используя статические массивы Arrays.toList(), можно преобразовать массив в список.
public class Unsupported {
private static String[] s = { "one", "two", "three", "four", "five", "six",
"seven", "eight", "nine", "ten", };
static List a = Arrays.toList(s);
static List a2 = Arrays.toList(new String[] { s[3], s[4], s[5] });
public static void main(String[] args) {
Collection1.print(a); // Iteration
System.out.println("a.contains(" + s[0] + ") = " + a.contains(s[0]));
System.out.println("a.containsAll(a2) = " + a.containsAll(a2));
System.out.println("a.isEmpty() = " + a.isEmpty());
System.out.println("a.indexOf(" + s[5] + ") = " + a.indexOf(s[5]));
// Traverse backwards:
ListIterator lit = a.listIterator(a.size());
while (lit.hasPrevious())
System.out.print(lit.previous());
System.out.println();
// Set the elements to different values:
for (int i = 0; i < a.size(); i++)
a.set(i, "47");
Collection1.print(a);
// Compiles, but won't run:
lit.add("X"); // Unsupported operation
a.clear(); // Unsupported
a.add("eleven"); // Unsupported
a.addAll(a2); // Unsupported
a.retainAll(a2); // Unsupported
a.remove(s[0]); // Unsupported
a.removeAll(a2); // Unsupported
}
}
Как видите, реально реализована только часть интерфейсов Collection и List. Оставшийся метод приводит к нежелательному состоянию, называемому UnsupportedOperationException. В классах коллекций, которые реализуют эти интерфейсы, поддержка этих методов может предоставляться, а может и не предоставляться. Вызов неподдерживаемого метода приводит к UnsupportedOperationException, что указывает на программную ошибку. Arrays.toList() создает список (список), полученный из массива фиксированной длины. Таким образом, поддерживаются только те операции, которые не изменяют длину массива. С другой стороны, если вы запрашиваете новый интерфейс для выражения другого типа поведения (возможно, называемый «FixedSizeList» — список фиксированной длины), вы рискуете столкнуться с более высоким уровнем сложности. Таким образом, когда вы попытаетесь использовать библиотеку в будущем, вы быстро не поймете, с чего начать. Для методов, которые принимают Collection, List, Set или Map в качестве параметров, в их документации должно быть указано, какие необязательные методы должны быть реализованы. Например, для сортировки требуется реализация методов set() и Iterator.set(), но не add() и remove().
Сортировка и поиск
множество
Класс Arrays предоставляет перегрузку для массивов всех примитивных типов данных.sort()和 binarySearch()
, которые также доступны для String и Object.
public class Array1 {
static Random r = new Random();
static String ssource = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
static char[] src = ssource.toCharArray();
// Create a random String
public static String randString(int length) {
char[] buf = new char[length];
int rnd;
for (int i = 0; i < length; i++) {
rnd = Math.abs(r.nextInt()) % src.length;
buf[i] = src[rnd];
}
return new String(buf);
}
// Create a random array of Strings:
public static String[] randStrings(int length, int size) {
String[] s = new String[size];
for (int i = 0; i < size; i++)
s[i] = randString(length);
return s;
}
public static void print(byte[] b) {
for (int i = 0; i < b.length; i++)
System.out.print(b[i] + " ");
System.out.println();
}
public static void print(String[] s) {
for (int i = 0; i < s.length; i++)
System.out.print(s[i] + " ");
System.out.println();
}
public static void main(String[] args) {
byte[] b = new byte[15];
r.nextBytes(b); // Fill with random bytes
print(b);
Arrays.sort(b);
print(b);
int loc = Arrays.binarySearch(b, b[10]);
System.out.println("Location of " + b[10] + " = " + loc);
// Test String sort & search:
String[] s = randStrings(4, 10);
print(s);
Arrays.sort(s);
print(s);
loc = Arrays.binarySearch(s, s[4]);
System.out.println("Location of " + s[4] + " = " + loc);
}
}
В main(), Random.nextBytes() Заполняет аргумент массива случайно выбранными байтами (нет соответствующего метода Random для создания массивов других примитивных типов данных). Когда у вас есть массив, вы можете видеть, что для выполнения sort() или binarySearch() вам нужно только выполнить вызов метода.Есть еще одно важное предостережение в отношении binarySearch(): если вы не вызовете sort() перед однократным выполнением binarySearch(), произойдет непредсказуемое поведение, включая даже бесконечные циклы.Сортировка и поиск в Strings аналогичны, но при запуске программы мы замечаем интересное явление: сортировка подчиняется лексикографическому порядку, то есть в наборе символов прописные буквы стоят перед строчными. Таким образом, все прописные буквы находятся в верхней части списка, за ними следуют строчные буквы — Z на самом деле стоит перед a . Даже телефонная книга, кажется, отсортирована таким образом.
- Сопоставимый и компаратор Если вы хотите отсортировать массив объектов, вам нужно решить проблему. На чем основано определение порядка двух Объектов? К сожалению, оригинальные разработчики Java не считали это важным вопросом, иначе он был бы определен в корневом классе Object. Одним из следствий этого является то, что сортировка объектов должна выполняться извне, и новая библиотека коллекций предоставляет стандартный способ сделать это (в идеале определить его в объектах). Для массивов объектов (и строк, которые, конечно, являются типом объекта) вы можете использовать sort() и заставить его принимать другой параметр: класс, который реализует интерфейс Comparator (то есть интерфейс «Comparator», часть новой библиотеки коллекций) объект и сравнить его с помощью единственного метода compare(). Этот метод использует два сравниваемых объекта как свои собственные параметры - **Если первый параметр меньше второго, он возвращает отрицательное целое число, если он равен, он возвращает ноль, если первый параметр больше второго , то Возвращает положительное целое число. **Исходя из этого правила, часть String приведенного выше примера можно переписать, чтобы сделать ее действительно алфавитной: Выполняя приведение к String, метод compare() выполняет тест «подсказки», чтобы убедиться, что он работает только с объектами String — система удачи обнаружит любые ошибки. После перевода обеих строк в нижний регистр метод String.compareTo() дает ожидаемый результат.Если вы выполняете sort() со своим собственным компаратором, вы должны использовать тот же самый компаратор при использовании бинарного поиска(). Класс Arrays предоставляет еще один метод sort(), который принимает один аргумент: массив объектов, но не компаратор. это Метод sort() также должен таким же образом сравнивать два объекта.Реализуя интерфейс Comparable, он использует «естественный метод сравнения», данный классу. Этот интерфейс содержит единственный метод, compareTo(), который может сравнивать объекты, возвращая отрицательное, нулевое или положительное число в зависимости от того, меньше, равно или больше аргумента, соответственно.
public class CompClass implements Comparable {
private int i;
public CompClass(int ii) {
i = ii;
}
public int compareTo(Object o) {
// Implicitly tests for correct type:258
int argi = ((CompClass) o).i;
if (i == argi)
return 0;
if (i < argi)
return -1;
return 1;
}
public static void print(Object[] a) {
for (int i = 0; i < a.length; i++)
System.out.print(a[i] + " ");
System.out.println();
}
public String toString() {
return i + "";
}
public static void main(String[] args) {
CompClass[] a = new CompClass[20];
for (int i = 0; i < a.length; i++)
a[i] = new CompClass((int) (Math.random() * 100));
print(a);
Arrays.sort(a);
print(a);
int loc = Arrays.binarySearch(a, a[3]);
System.out.println("Location of " + a[3] + " = " + loc);
}
}
- список Список можно сортировать и искать так же, как массив. Статические методы для сортировки и поиска в списках содержатся в классе Collections, но по сигнатурам они аналогичны сигнатурам Arrays: sort(List) для сортировки списка объектов, реализующих Comparable; binarySearch(List,Object) для поиска объекта в массиве. list; sort(List,Comparator) сортирует список с помощью «компаратора»; и binarySearch(List,Object,Comparator) находит объект в этом списке
public class ListSort {
public static void main(String[] args) {
final int SZ = 20;
// Using "natural comparison method":
List a = new ArrayList();
for(int i = 0; i < SZ; i++)
a.add(new CompClass(
(int)(Math.random() *100)));
Collection1.print(a);
Collections.sort(a);
Collection1.print(a);
Object find = a.get(SZ/2);259
int loc = Collections.binarySearch(a, find);
System.out.println("Location of " + find +
" = " + loc);
// Using a Comparator:
List b = new ArrayList();
for(int i = 0; i < SZ; i++)
b.add(Array1.randString(4));
Collection1.print(b);
AlphaComp ac = new AlphaComp();
Collections.sort(b, ac);
Collection1.print(b);
find = b.get(SZ/2);
// Must use the Comparator to search, also:
loc = Collections.binarySearch(b, find, ac);
System.out.println("Location of " + find +
" = " + loc);
}
}
Использование этих методов точно такое же, как и в массивах, за исключением того, что массив заменяется списком. TreeMap также должен сортировать свои собственные объекты в соответствии с Comparable или Comparator. Утилиты в классе Collections: enumeration(Collection) создает перечисление примитивного стиля для аргументов max(Collection), min(Collection) дают наибольший или наименьший элемент в аргументе, используя метод естественного сравнения объектов в коллекции max(Collection,Comparator), min(Collection,Comparator) используют компаратор для получения наибольшего или наименьшего элемента в коллекции nCopies(int n, Object o) возвращает неизменяемый список длины n, все дескрипторы которого указывают на o subList(List,int min,int max) Возвращает новый список, отодвинутый назад от указанного списка аргументов. Воспринимайте этот список как "окно", которое начинается с индекса min и заканчивается непосредственно перед max Обратите внимание, что min() и max() работают с объектами Collection, а не со списками, поэтому не беспокойтесь о том, нужно ли сортировать Collection (как указывалось ранее, перед выполнением binarySearch() — бинарного поиска — необходимо выполнить sort() в списке или массиве)
1. Сделайте коллекцию или карту неизменной
Часто бывает выгоднее создать версию коллекции или карты «только для чтения». Класс Collections позволяет нам добиться этого, передав исходный контейнер в метод и заставив его вернуть версию, доступную только для чтения. Существует четыре варианта этого метода: для коллекции (если вы не хотите рассматривать коллекции как более специальный тип), списка, набора и карты.
public class ReadOnly {
public static void main(String[] args) {
Collection c = new ArrayList();
Collection1.fill(c); // Insert useful data
c = Collections.unmodifiableCollection(c);
Collection1.print(c); // Reading is OK
// ! c.add("one"); // Can't change it
List a = new ArrayList();
Collection1.fill(a);
a = Collections.unmodifiableList(a);
ListIterator lit = a.listIterator();
System.out.println(lit.next()); // Reading OK
// ! lit.add("one"); // Can't change it
Set s = new HashSet();
Collection1.fill(s);
s = Collections.unmodifiableSet(s);
Collection1.print(s); // Reading OK
// ! s.add("one"); // Can't change it
Map m = new HashMap();
Map1.fill(m, Map1.testData1);
m = Collections.unmodifiableMap(m);
Map1.print(m); // Reading OK
// ! m.put("Ralph", "Howdy!");
}
}
В каждом случае контейнер должен быть заполнен действительными данными, прежде чем он официально станет доступным только для чтения. После загрузки рекомендуется заменить существующий дескриптор на дескриптор, полученный с помощью «неизменяемого» вызова. Это эффективно предотвращает непреднамеренное изменение содержимого после того, как оно станет немодифицируемым.С другой стороны, инструмент также позволяет нам сохранять изменяемый контейнер закрытым внутри класса и возвращать доступный только для чтения дескриптор этого контейнера из вызова метода. Таким образом, хотя мы можем изменить его в классе, его сможет прочитать только кто-то еще. Вызов «неизменяемого» метода для определенного типа не вызывает проверки во время компиляции, но как только что-либо изменится, вызов метода, изменяющего конкретный контейнер, вызовет исключение UnsupportedOperationException.
2. Синхронизация коллекции или карты
Здесь вам просто нужно заметить, что класс Collections предоставляет способ автоматической синхронизации всего контейнера. Его синтаксис аналогичен «немодифицируемому» методу:
public class Synchronization {
public static void main(String[] args) {
Collection c = Collections.synchronizedCollection(new ArrayList());
List list = Collections.synchronizedList(new ArrayList());
Set s = Collections.synchronizedSet(new HashSet());
Map m = Collections.synchronizedMap(new HashMap());
}
}
Суммировать
(1) Массив содержит числовые индексы объектов. Он содержит объект известного типа, поэтому при поиске объекта вам не нужно приводить результат. Массивы могут быть многомерными и содержать основные типы данных. Однако после создания размер изменить нельзя. (2) Векторы также содержат числовые индексы объектов — думайте о массивах и векторах как о коллекциях с произвольным доступом. Когда мы добавляем больше элементов, Вектор может автоматически изменять свой размер. Но Vector может содержать только дескрипторы объектов, поэтому он не может содержать примитивные типы данных, а когда дескриптор объекта удаляется из коллекции, результат должен быть смоделирован. (3) Hashtable (хеш-таблица) — это тип Dictionary (словарь), который представляет собой способ связывания объектов (а не чисел) с другими объектами. Хеш-таблица также поддерживает произвольный доступ к объектам, и, по сути, вся ее конструктивная схема подчеркивает «высокую скорость» доступа. (4) Стек представляет собой очередь «последний пришел — первый вышел» (LIFO). С помощью Hashtable в него можно поместить что угодно и получить его очень быстро; с помощью Enumeration можно просмотреть последовательность и выполнить определенное действие над каждым элементом. Это достаточно мощный инструмент. Но Hashtable не имеет понятия «порядок». Векторы и массивы обеспечивают линейный порядок, но вставка элемента в середину любого из них обычно обходится дорого. Кроме того, очереди, очереди распаковки, приоритетные очереди и деревья включают в себя «упорядочение» элементов, а не просто размещение их так, чтобы их можно было позже найти или переместить в линейном порядке.
3. Сравнение и сводка по каждой категории коллекций
Набор (Set): объекты в наборе не располагаются каким-либо определенным образом, данные обрабатываются в соответствии со значением индекса, и не может быть повторяющихся элементов. Список (список): объекты в последовательности хранятся линейным образом, данные управляются значением индекса, и могут быть повторяющиеся элементы. Карта: каждый элемент карты представляет собой пару «имя-значение», имя не может повторяться, значение может повторяться, а имя соответствует уникальному значению.
Итератор Итератор
Итераторы получают все объекты в коллекции один за другим по порядку и являются стандартным механизмом доступа к каждому элементу в коллекции. Создание итератора: метод iterator() интерфейса Collection возвращает итератор. Iterator it=test.iterator(); // Преобразование объекта тестовой коллекции в итератор Общие методы итераторов:
hasNext() //Определяем, есть ли следующий элемент в итераторе next() //возвращает следующий элемент итерации Remove() //Удаляем только что возвращенный элемент из итератора
public interface Iterator {
boolean hasNext();
Object next();
void remove(); // Optional
}
При вызове метода remove() метод next() должен быть вызван один раз. Метод remove() фактически удаляет последний возвращенный элемент.
Перечислите распространенные методы
void add(int index, Object element) : добавить элемент объекта в индекс позиции boolean addAll(int index, Collection collection): добавить все элементы в коллекцию контейнеров после позиции индекса. Object get(int index): вынуть элемент в позиции индекса index. int indexOf (элемент объекта): найти первое вхождение элемента объекта в списке. int lastIndexOf (элемент объекта): найти последнюю позицию элемента объекта в списке. Object remove(int index) : удалить элемент в позиции индекса ListIterator listIterator(int startIndex) : возвращает элемент списка ListIterator, начинающийся с startIndex. List subList(int fromIndex, int toIndex): возвращает список подсписков, элементы сохраняются как элементы от fromIndex до toIndex.
ArrayList
Думайте об этом как о массиве, емкость которого автоматически увеличивается. Используйте toArray() ArrayList для возврата массива. Arrays.asList() возвращает список. Итераторы дают нам общий способ доступа к элементам коллекции. ArrayList может автоматически увеличивать емкость ArrayList.ensureCapacity(int minCapacity) Сначала получите длину oldCapacity текущего свойства elementData . Затем решите, расширять ли емкость, определяя, какой из параметров oldCapacity и minCapacity больше.Если minCapacity больше, чем oldCapacity, мы расширяем текущий объект List. ** Стратегия расширения: ** Берем большее из значений (oldCapacity * 3)/2 + 1 и minCapacity. Затем используйте метод копирования массива для переноса ранее сохраненных данных в новый объект массива.Если minCapacity не больше, чем oldCapacity, то расширение выполняться не будет.
LinkedList
LinkedList реализован с использованием двойного циклического связанного списка. Используя LinkedList, можно реализовать стек, очередь и двустороннюю очередь. Он имеет методы addFirst(), addLast(), getFirst(), getLast(), removeFirst(), removeLast() и т. д.
Сравнение ArrayList и LinkedList
1. ArrayList — это структура данных, основанная на динамических массивах, а LinkedList — структура данных, основанная на связанных списках. 2. Для получения и установки произвольного доступа ArrayList лучше, чем LinkedList, потому что LinkedList должен перемещать указатель. 3. Для операций добавления и удаления больше подходит LinedList, потому что ArrayList должен перемещать данные. Старайтесь избегать обхода и удаления коллекции одновременно. так как это изменит размер коллекции;
for( Iterator<ComType> iter = ComList.iterator(); iter.hasNext();){
ComType com = iter.next();
if ( !com.getName().contains("abc")){
ComList.remove(com);}
}
рекомендовать:
for( Iterator<ComType> iter = ComList.iterator(); iter.hasNext();){
ComType com = iter.next();
if ( !com.getName().contains("abc")){
iter.remove(com); }
Неограниченное добавление элемента в lst неизбежно приведет к тому, что lst будет занимать слишком много памяти.
Карта общих методов
Общий метод:
Объект помещается (ключ объекта, значение объекта): используется для хранения пары ключ-значение на карте. Удалить объект (ключ объекта): удалить пару ключ-значение в соответствии с ключом (ключом) и вернуть значение void putAll (сопоставление карты): сохранить элементы на другой карте в текущей карте. void clear() : очищает элементы на текущей карте. Получить объект (ключ объекта): получить соответствующее значение в соответствии с ключом (ключом). логическое значение containsKey (ключ объекта): определить, существует ли ключ (ключ) на карте. логическое значение containsValue (значение объекта): определить, есть ли значение на карте (значение) public Set keySet() : возвращает все ключи (ключи) и сохраняет их с помощью контейнера Set. public Collection values(): возвращает все значения (Value) и сохраняет их в Collection. public Set entrySet() : возвращает набор элементов, реализующих интерфейс Map.Entry.
HashMap
Карта в основном используется для хранения пар ключ (ключ) значение (значение) и получения значения в соответствии с ключом, поэтому повторение ключа запрещено, но разрешено повторение значения. HashMap — одна из наиболее часто используемых карт, она хранит данные в соответствии со значением HashCode ключа, и его значение может быть получено непосредственно по ключу с высокой скоростью доступа. HashMap позволяет ключу одной записи быть не более чем Null, значение нескольких записей может быть Null; HashMap не поддерживает синхронизацию потоков, то есть несколько потоков могут записывать HashMap одновременно в любое время, это может привести к несогласованности данных. Если требуется синхронизация, вы можете использовать метод synchronizedMap Collections, чтобы сделать HashMap синхронным, или использовать ConcurrentHashMap. При использовании HashMap, когда объект используется в качестве значения ключа, необходимо переопределить как equals(), так и hashCode().
LinkedHashMap и HashMap, сравнение TreeMap
Hashtable похож на HashMap, он наследуется от класса Dictionary, разница в том, что он не позволяет ключу или значению записей быть пустыми, поддерживает синхронизацию потоков, то есть только один поток может писать Hashtable в любой момент времени, поэтому он также заставляет Hashtable писать Entry будет медленнее. Hashmap — одна из наиболее часто используемых карт.Он хранит данные в соответствии со значением HashCode ключа, и его значение можно получить непосредственно по ключу.Он имеет высокую скорость доступа.При обходе,Порядок получения данных абсолютно случайный.LinkedHashMap сохраняет порядок вставки записей,При использовании Iterator для обхода LinkedHashMap запись, полученная первой, должна быть вставлена первой.Вы также можете использовать параметры во время построения для сортировки по количеству приложений. Это будет медленнее, чем HashMap, при обходе, но есть исключение: когда HashMap имеет большую емкость, а фактические данные малы, обход может быть медленнее, чем LinkedHashMap, потому что скорость обхода LinkedHashMap связана только с фактическими данными. , и не имеет ничего общего с емкостью.Скорость обхода HashMap связана с его емкостью. TreeMap реализует интерфейс SortMap, который может сортировать записи, которые он сохраняет, в соответствии с ключом. По умолчанию используется сортировка в порядке возрастания значения ключа. Вы также можете указать компаратор сортировки. Когда итератор используется для обхода TreeMap, полученный записи сортируются.Мы чаще всего используем HashMap. Пары ключ-значение, хранящиеся в HashMap, случайны, когда их извлекают. Чтобы вставлять, удалять и находить элементы в Map, лучше всего подходит HashMap. TreeMap извлекает отсортированные пары ключ-значение. но если вы хотите нажатьПеремещение ключей в естественном или пользовательском порядке, то TreeMap будет лучше. LinkedHashMap является подклассом HashMap. Если порядок вывода должен совпадать с порядком ввода, можно использовать LinkedHashMap. Его также можно упорядочить в порядке чтения, который можно использовать в пуле соединений.
Использование набора
Повторяющиеся элементы не допускаются Добавлены ограничения на методы add(), equals() и hashCode(). HashSet и TreeSet являются реализациями Set Набор — «HashSet LinkedHashSet Сортированный набор —> Набор деревьев
HashSet
public boolean contains(Object o): возвращает true, если набор содержит указанный элемент public Iterator iterator() возвращает итератор элементов в наборе public Object[] toArray() : возвращает массив, содержащий все элементы набора public Object[] toArray(Object[] a) : возвращает массив, содержащий все элементы набора, тип возвращаемого массива во время выполнения — тип времени выполнения указанного массива public boolean add(Object o): если указанный элемент не существует в наборе, добавить его в набор public boolean remove(Object o): если указанный элемент существует в наборе, удалить из набора public boolean removeAll(Collection c): если набор содержит указанную коллекцию, удаляет все элементы указанной коллекции из набора public boolean containsAll(Collection c): возвращает true, если набор содержит все элементы указанной коллекции. Если указанный набор также является набором, метод возвращает значение true, только если он является подмножеством текущего набора.
HashSet, реализующий интерфейс Set, реализуется с опорой на HashMap. Мы должны определить hashCode() и equals() для каждого объекта, который будет храниться в хеш-таблице. Как HashSet фильтрует повторяющиеся элементы Вызовите элемент HashCode, чтобы получить хеш-код — «судя, равны ли хэш-коды, если они не равны, введите —», если они равны, они будут судить, равны ли они после equals().
TreeSet
TreeSet реализован с использованием TreeMap. TreeSet — это упорядоченный набор. Элементы в TreeSet будут расположены в порядке возрастания. По умолчанию используется естественный порядок, что означает, что элементы в TreeSet должны реализовывать интерфейс Comparable. Мы можем передать компаратор, который реализует интерфейс Comparator, когда построение объекта TreeSet.
HashSet против TreeSet против LinkedHashSet
HashSet не может гарантировать порядок расположения элементов, порядок может меняться, он не синхронизируется, элементы набора могут быть нулевыми, но можно разместить только один нуль TreeSet — единственный класс реализации интерфейса SortedSet, TreeSet может гарантировать, что элементы коллекции находятся в отсортированном состоянии. TreeSet поддерживает два метода сортировки: естественную сортировку и пользовательскую сортировку, где естественная сортировка является методом сортировки по умолчанию. Объекты одного класса должны быть добавлены в TreeSet. Способ, которым TreeSet определяет, что два объекта не равны, заключается в том, что два объекта возвращают false с помощью метода equals или не возвращают 0 с помощью метода CompareTo.естественный порядокЕстественная сортировка использует метод CompareTo(Object obj) сортируемых элементов для сравнения соотношения размеров между элементами, а затем сортирует элементы в порядке возрастания.пользовательская сортировкаЕстественная сортировка заключается в сортировке в порядке возрастания в соответствии с размером элементов коллекции.Если вы хотите настроить сортировку, вы должны использовать интерфейс Comparator и реализовать метод int compare(To1,To2) Коллекция LinkedHashSet также определяет место хранения элемента в соответствии со значением hashCode элемента, но также использует связанный список для поддержания порядка элементов. Это создает впечатление, что элементы хранятся в порядке вставки, то есть при обходе коллекции LinkedHashSet будет обращаться к элементам коллекции в том порядке, в котором они были добавлены.LinkedHashSet имеет лучшую производительность, чем HashSet, при итеративном доступе ко всем элементам в наборе, но немного уступает HashSet при вставке.
Использованная литература:
- "идеи программирования на Java"
- https://blog.csdn.net/u012124438/article/details/76698331
- http://www.cnblogs.com/xiaoshitoutest/p/6963798.html
В статье есть что-то неуместное, пожалуйста, поправьте меня, вы также можете обратить внимание на мой паблик WeChat:
好好学java
, для получения качественных ресурсов.