Туповатый вопрос, но всё же: как правильно реализовывать в Java представление графа списками смежности?
Пишу
ArrayList<Integer>[] graph = new ArrayList<Integer>[N];
for(int i=0; i<N; i++)
graph[i] = new ArrayList<Integer>();
вообще не компилирует говорит ошибку "generic array creation".
Пишу
List[] graph = new List[N];
for(int i=0; i<N; i++)
graph[i] = new ArrayList<Integer>();
компилирует но говорит предупреждение "Note: Main.java uses unchecked or unsafe operations.".
А как сделать по-хорошему, чтоб не было предупреждений?
Просьба не объяснять, как делать в других языках программирования. Вопрос именно по реализации именно в Java.
В прошлой правке абсолютный бред. (Правка сделана после сообщения ниже)
Рефлексы чтения сработали :(. [] после N лет С++ выглядит примерно как медведь, идущий лунной походкой.
Когда писал на Java, она еще была Java 4...
===
for (int i=0;i<n;i++)
g[i]=new ArrayList<Integer>();
Полный текст --
[cut]
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StreamTokenizer;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
public class Main {
static int nextInt(StreamTokenizer in) {
try {
in.nextToken();
return (int) in.nval;
} catch (IOException ex) {
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
return 0;
}
}
public static void main(String[] args) throws FileNotFoundException {
StreamTokenizer in = new StreamTokenizer(new FileReader("input.txt"));
PrintWriter out = new PrintWriter(new File("output.txt"));
int TEST_NUM = nextInt(in);
for(int the_test=0; the_test<TEST_NUM; the_test++) {
int N = nextInt(in), M = nextInt(in);
ArrayList<Integer>[] graph = new ArrayList[N];
for(int i=0; i<N; i++)
graph[i] = new ArrayList<Integer>();
for(int k=0; k<M; k++) {
int u = nextInt(in), v = nextInt(in);
graph[u].add(new Integer(v));
}
int[] st = new int[N];
int[] d = new int[N];
Arrays.fill(st, 0);
Arrays.fill(d, 987654321);
int start = nextInt(in);
st[start] = 1;
d[start] = 0;
Queue<Integer> qqq = new ArrayDeque<Integer>();
qqq.add(start);
while(!qqq.isEmpty()) {
int u = qqq.poll();
for(int w : (ArrayList<Integer>) graph[u])
if(st[w]==0) {
qqq.add(w);
st[w] = 1;
d[w] = d[u] +1;
}
}
for(int i=0; i<N; i++)
out.print(d[i] + " ");
out.println();
}
out.close();
}
}
Точные требования с форматам ввода-вывода и т.д. -- http://informatics.mccme.ru/moodle/mod/statements/view3.php?id=3311&chapterid=3489#1
ArrayList<Integer>[] graph = new ArrayList[N];
на
@SuppressWarnings("unchecked")
ArrayList<Integer>[] graph = new ArrayList[N];
раз уже так этот warning ненавистен
Где именно я не прав, считая, что когда неправильное действие вообще не пропускается компилятором, то это ещё более безопасно?
Я не хочу отключать проверки. Я, наоборот, хочу добиться, чтоб оказались невозможными технические ошибки вследствие того, что я случайно что-то не так потайпкастил.
Очень прошу тех, кому хочется минусовать ДАННЫЙ пост, подтверждать своё минусование более развёрнутыми аргументами, что тут неправильно, указывая, сколько в своей жизни они написали проектов размером хотя бы 1000 строк.
Еще более безопасно вообще не запускать и не компилировать никакой код. А если серьезно, то что вы называете неправильным действием? Полностью исключить ошибки времени исполнения компилятор не сможет, он даже не всегда сможет понять используется какой-либо кусок кода программы или нет. Это следует из halting problem.
там после этой ошибки написано "перекомпилируйте с ключом xlint:unchecked чтобы посмотреть где происходят эти преобразования"
Например, в этой строчке:
ArrayList<Integer>[] graph = new ArrayList[N];
То что женеричный массив нельзя создавать не означает что его надо создать без типа. Используйте вместо него ArrayList<ArrayList<Integer>> если уж очень хочется. Вообще если вам настоящий список нужен, то надо пользоваться LinkedList а не ArrayList, иначе всё можно было просто двумерным массивом представить и не мучаться.
Вообще-то... Лучше уж либо с женериками разобраться, либо писать без них. Для олимпиадных задач этого бы хватило. ;-)
UPD: Писать без них я имею в виду что не нужно обязательно параметризовать коллекции. Напишите
ArrayList[] graph = new ArrayList[N];
И всё будет нормально. В маленькой программе для личных потребностей это вполне допустимо.
Ещё одно "промышленное замечание"
Не стоит создавать ArrayList<Integer> ar = new ArrayList<Integer>(), а лучше
List<Integer> ar = new ArrayList<Integer>();
Причина в том, что этот список во втором случае можно будет создать разными способами, т.е. отделить абстракцию (List) от реализации ArrayList(). Но шаблоны лучше все равно использовать.
Любые ситуации когда параметризация типов стирается предлагается старательно избегать за исключением случаев когда надо совместиться со старым (java 1.4) кодом.
double post
ArrayList<Integer>[] graph = new ArrayList[N];
-- огромное спасибо, похоже, это на 99% закрывает вопрос.
А насчёт остального текста сообщения -- что-то то ли плохо написано, то ли я плохо читаю, но сначала я его совершенно не понял. Особенно меня огорчило утверждение "Можно создать ArrayList<String> и присвоить его в ArrayList<Integer> и это будет компилироваться.".
Но в том-то и дело, что при объявлении
ArrayList<Integer>[] graph = new ArrayList[N];
и дальнейшей попытке использования
for(String s : (ArrayList<String>)graph[u])
ошибка компиляции таки возникает, сообщая мне, что у меня бардак с типами. Что, насколько я понимаю, хорошо и правильно. А из Вашего сообщения я сначала понял в точности наоборот, и поначалу даже не проверил.
List<Integer> listI = new LinkedList<Integer>();
listI.add(7);
List list = listI;
List<String> listS = list;
listS.add("Java");
System.out.println(listS);
При компиляции не будет ошибки, однако будет предупреждение которое можно увидеть с помощью -XLint:unchecked
Это сделано для совместимости со старыми версиями (до 5-й Java).
Но "за такое" меня на одном собеседовании (в некое ЗАО Астротэк) два года назад не взяли. Именно спросили - "какого многоточия вы использовали самодельные списки вместо структур из STL (я тогда ещё на C++ собеседовался). Ни на что не намекаю, просто на всякий случай рассказываю... Хотя не единственная проблема была в моём решении... ;-)
даблпост
Впрочем, ArrayList или LinkedList, как мне кажется, совершенно не влияет на суть основного вопроса.
Чем всё-таки не устраивает массив ArrayList'ов ?
Вроде операции с графом реализовывать проще.
Насчёт перфоманса примерно одинаково, но вообще у списков смежности есть потенциальное преимущество в том, что смежные с вершиной рёбра идут последовательно в отличие от одного большого списка, где нет такого порядка.
Если нужно полноценное удаление рёбер, то можно по-аналогии сделать массив HashSet'ов, если перфоманс не очень важен.
Интересный вопрос - как сделать быструю структуру для добавления/удаления рёбер/вершин.
В массив HashSet-ов не верю категорически. Он будет либо жрать немеряное количество памяти, либо будут многократно перераспределяться хэш-таблицы. В первом случае лучше уж элементарная квадратная матрица смежности. Во втором -- не знаю, но тоже как-то нехорошо.
Кстати, сам когда-то придумал тренировочную задачу http://informatics.mccme.ru/moodle/mod/statements/view3.php?id=3311&chapterid=3490#1, заставляющую писать (на плюсах) либо vector<multiset<int> > либо vector<map<int,int> >, а недавно попытался переписать на джаву (как Map<Integer,Integer>[]) -- так скорость работы падает даже не в несколько раз, а то ли во много десятков раз то ли асимптотически (0.688 сек где на плюсах 0.027), при том что чтение/вывод -- StreamTokenizer(FileReader), PrintWriter(File).
А что на счет
class IntArray extends ArrayList<Integer>{}
IntArray ar[] = new IntArray[10];
?
Появление лишнего кода - проблема? выкидываем все паттерны, они создают лишний исходник!
Зато все проверки времени компиляции осуществляются во время компиляции, а не когда программа повалится с рантаймом.
Заметим следующие факты:
1) Без использования специализации шаблона в Java весь код шаблона может быть реализован одним бинарником. Никто не мешает рассматривать HashSet<Integer> и HashSet<String> как разные классы с общим объектным кодом.
2) Использование специализации шаблона можно рассматривать как создание дополнительной версии функции/класса.
Таким образом, компилятор не будет генерировать "лишний" бинарник, т.е. размер бинарника будет O(1)*размер написанного пользователем кода.
Возникают дополнительные временные затраты на поиск релевантного метода, однако, по-видимому, их можно устранить кэшированием версий класса уже на момент рантайма.
Я что-то упускаю?
Тип C определён параметром конструктора (String) а тип B указан явно (Thread)... Или что имелось в виду сделать?
А если ты хочешь чтобы C был не String а просто Object, но подать на вход надо String?
Дык покастить к объекту прямо там?
new A<Thread>((Object) "blabla");
UPD: Сорри, я это написал к первой версии коммента но из-за тормозов оно проявилось только сейчас... ;-)
Да ведь C к женерикам не имеет отношения - это типовой параметр метода.// печатает 4 раза "C"
public class A<B>{
public <C>A(C d){
System.out.println(getClass().getConstructors()[0]
.getTypeParameters()[0]);
} // A
public static void main(String... args) {
A a = new A<Thread>("blabla");
new <String> A<Thread>("blabla");
new A<Thread>((Object) "blabla");
new <Object> A<Thread>("blabla");
} // main
};
//ядлиннаястрочкаприветядлиннаястрочкаприветядлиннаястрочкаура
На всякий случай: я проминусовал только самый первый пост ((user:anonymous)) . Остальное не я.
Type safety: The expression of type ArrayList[] needs unchecked conversion to conform to ArrayList[]
Раз уж пост все равно подняли — очень хорошая идея использовать интерфейс вместо конкретной имплиментации, так как если надо будет поменять имплиментацию, это надо будет сделать в одном месте — вызове конструктора
То есть я бы писал таки
С другой стороны, могут оказаться нужными методы конкретной реализации...