12.03.10

Прекрасные примеры визуализации данных

Есть такой вполне объяснимый феномен: если данные красиво визуализированы, то волей-неволей вы задерживаете на визуализации свой взгляд, вне зависимости от того, нужны вам эти данные или нет. Чего не скажешь об обыденных графиках и унылых схемах.
В связи с чем, я решил собрать коллекцию прекрасных попыток визуализации. Чтобы вдохновляться. Помогает. Поэтому делюсь. Возможно, гляда на эти красоты, и вам прийдет в голову замечательная концепция.
Warning: большой трафик.
В большинстве случаев, картинки кликабальны и ведут на источник.






























Если есть интересные примеры визуализации, не поленитесь, поделитесь в комментариях.
Удачной творческой пятницы вам.

11.03.10

преобразование объектов Java в формат JSON и обратно

В поисках библиотеки для работы с JSON в Java, наткнулся на интересный проект google-gson.
В кратце, Gson - это Java библиотека, которая может быть использована для преобразования Java объектов в представление JSON, а так же для обратного преобразования JSON в объекты Java. Gson может работать с произвольными объектами Java, включая объекты к исходным кодам которых вы не имеете доступа.

Существует несколько подобных проектов, но большинство из них требует размещения аннотаций в исходном коде, для того, чтобы была возможность сериализации в JSON, что является проблемой, когда доступа к исходным кодам нет.

Основыми целями Gson являются:

  • простота использования, методы toJson() и fromJson() для преобразования Java объектов в JSON и обратно;

  • работа с уже существующими немодифицируемыми объектами, доступ к исходным кодам которых ограничен;

  • широкая поддержка Java Generics

  • представление данных в виде ваших собственных объектов;

  • поддержка произвольно сложных объектов (с глубокой иерархией наследования и с широким использованием общих типов)



Это все лирика, более подробно читайте на сайте проекта, я вам просто приведу пример.
За основу я возьму JSON из Twitter API, который возвращает текущие тренды в твиттере, выглядит этот JSON так:
{
  "as_of": "Thu, 11 Mar 2010 15:48:02 +0000",
  "trends" :
   [
     {
      "name": "#textsihate",
      "url": "search.twitter.com/search?q=%23textsihate"
     },
  ...
     {
      "name": "President Pinera's",
      "url": "search.twitter.com/search?q=%22President+Pinera%27s%22"
     }
   ]
}

* This source code was highlighted with Source Code Highlighter.

Я обрезал JSON, всего возвращается 10 элементов trends, по этому адресу можно посмотреть весь код.
Весьма несложная структура JSON выбрана для того, чтобы было проще понять, как эта библиотека работает.
Теперь собственно к коду, который прочитает этот JSON, разберет его и вернет вам Java класс с полученным содержимым:
package org.divenvrsk.gson.example;

import com.google.gson.Gson;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;

public class GSonTest {
  public static final String TWITTER_TRENDS_URL = "http://search.twitter.com/trends.json";

  public static void main(String[] argc) {
    StringBuffer buffer = null;
    try {
      URL url = new URL(TWITTER_TRENDS_URL);
      BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()));
      String inputLine;
      buffer = new StringBuffer();
      while ((inputLine = in.readLine()) != null) {
        buffer.append(inputLine);
      }
      in.close();
    } catch (IOException e) {
      e.printStackTrace();
    }

    TwitterTrends trends = new Gson().fromJson(buffer.toString(), TwitterTrends.class);
    for (int counter = 0; counter < trends.getTrends().length; counter++) {
      System.out.println(trends.getTrends(counter));
    }
  }

  static class TwitterTrends {
    private String as_of;
    private Trends[] trends;

    public String getAs_of() {
      return as_of;
    }

    public void setAs_of(String as_of) {
      this.as_of = as_of;
    }

    public Trends[] getTrends() {
      return trends;
    }

    public Trends getTrends(int counter) {
      return trends[counter];
    }

    public void setTrends(Trends[] trends) {
      this.trends = trends;
    }

    public String toString() {
      return "Trends at " + as_of + ". Count: " + trends.length;
    }

    static class Trends {
      private String name;
      private String url;

      public String getName() {
        return name;
      }

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

      public String getUrl() {
        return url;
      }

      public void setUrl(String url) {
        this.url = url;
      }

      public String toString() {
        return "name: " + name + "; url: " + url;
      }
    }
  }
}


* This source code was highlighted with Source Code Highlighter.

Результатом выполнения этого кода, будет подобный список:

name: #textsihate; url: http://search.twitter.com/search?q=%23textsihate
name: #nowplaying; url: http://search.twitter.com/search?q=%23nowplaying
name: #textihate; url: http://search.twitter.com/search?q=%23textihate
name: #dontcallyourself; url: http://search.twitter.com/search?q=%23dontcallyourself
name: Chile; url: http://search.twitter.com/search?q=Chile
name: #justcausewecool; url: http://search.twitter.com/search?q=%23justcausewecool
name: Betty White; url: http://search.twitter.com/search?q=%22Betty+White%22
name: SXSW; url: http://search.twitter.com/search?q=SXSW+OR+%23sxsw
name: Santiago; url: http://search.twitter.com/search?q=Santiago
name: USGS; url: http://search.twitter.com/search?q=USGS

Как видно из кода, всё что от вас требуется, это реализовать класс, который будет повторять структуру желаемого JSON и сгенерировать геттеры и сеттеры для всех объектов (класс TwitterTrends в нашем случае), затем вы получаете нужный вам JSON и вызываете метод fromJson(...) в качестве параметров указав источник данных и класс для сериализации:
TwitterTrends trends = new Gson().fromJson(buffer.toString(), TwitterTrends.class);

* This source code was highlighted with Source Code Highlighter.
И вуаля, у вас полноценный объект Java, с которым можно работать. К примеру, в случае с твиттером, где API стандартизировано и упорядочено, очень удобно вытаскивать данные таким способом, получая полноценный объект для работы, а не ворочая кучу строчек, как происходит в большинстве случаев. Так же, очень удобно формировать такой класс в коде, привести его к JSON и отправить POST в твиттер, не переживая за формат данных.

В общем, мне пригодилась эта библиотека, может быть понравится и вам.
Спасибо за внимание.

07.03.10

различия между API Android SDK Platform

Когда я в двадцатый раз принялся искать какие пакеты в какой версии Android SDK были добавлены и в какой версии SDK появился тот или иной метод, я решил нарисовать себе табличку с этими самыми изменениями. Сказано - сделано.


Немного о самом псевдографике-табличке:
- охвачены изменения начиная с версии Android Platfrom SDK 1.5 (API Level 3) и вплоть до версии 2.1 (API Level 7);
- на каждое изменение есть ссылка на описание в официальной документации;
- указаны все вновь добавленные методы и интерфейсы со ссылками на их описание (я специально не стал указывать классы и интерфейсы, в которых лишь произошли изменения, чтобы не загромождать это дело);
- если в пакете произошли важные изменения, но при этом не были добавлены новые методы, то такой пакет отмечен иконкой "восклицательный знак", чтобы привлечь ваше внимание.

Визуально думаю будет понятнее:


Такая штука не идеальна, я понимаю, но мне очень экономит время при поиске соответствия "метод - версия SDK" и быстрому переходу к документации.

Ссылки:
- PDF (2.7Mb, если кто-нибудь сожмет - буду благодарен);
- Online Flash версия (1.8Mb).

09.02.10

завтра Google начнет делать из Gmail социальную сеть?

Сервис Gmail станет следующим большим шагом Google в стройный ряд социальных ресурсов. Согласно сведениям The Wall Street Journal, популярный почтовый сервис скоро обзаведется дополнительными возможностями, такими как обмен контентом с друзьями и обновление статуса пользователя.

Как отмечает WSJ, пользователи Gmail уже могут изменять свой статус, используя для этого чат встроенный в веб интерфейс Gmail. Сейчас, эта функция больше походит на статусы традиционных клиентов мгновенных сообщений, однако, после нового шага к социальной платформе, Gmail предоставит возможность просматривать единый список обновления статусов ваших друзей, отсортированный по времени добавления, как это происходит на Facebook или в Twitter'е.

Обновление статусов будут происходить как непосредственно из Gmail, так и из ряда других сервисов. По мнению WSJ, принадлежащие Google сервисы YouTube и Picasa так же будут интегрированы в эту систему. Несомненно, загадкой является то, будет ли эта система от Google поддерживать интеграцию с Twitter'ом и Facebook'ом.

Если так, то новый сервис Google скорее будет походить на TweetDeck или Seesmic, т.е. будет просто агрегировать активность ваших друзей из различных социальных сервисов в одном месте, а так же предоставлять возможность обновлять статусы в этих службах непосредственно из Gmail. Если же нет, то новый сервис Gmail можно рассматривать, как серьезного конкурента Twitter'у и Facebook'у, учитывая многомиллионную аудиторию почтового сервиса от Google, приверженцы которого вполне могут создать социальный медиа сервис нового поколения.

Как бы то ни было, проблема с Gmail заключается в том, что исторически так сложилось, что люди добавляются к вам в список контактов автоматически, исходя из того, с кем вы ведете переписку по почте. Следовательно, этот список контактов зачастую разительно отличается от ваших реальных друзей, пусть и в социальных сетях, где вы устанавливаете отношения непосредственно по собственному желанию.

Иначе говоря, в списке ваших контактов в Gmail могут оказаться совсем не те люди, с которыми вам бы хотелось поделиться своими статусами, фотографиями или видео. Этот вопрос не следует упускать из виду при оценке нового сервиса Google, который, как мы надеемся, должен быть скоро анонсирован.

тут перевод заканчивается и говорит переводчик:
Подобные слухи ходили давно. Есть Orkut, очень популярный в Бразилии; есть задатки социальных движений на YouTube и уж точно в Google Reader. Осталось только по умному, рассудительно и не спеша, соединить это в единое целое. А уж рассудительности Google не занимать.
Кто за?

06.02.10

сайт о Владимире Кобрине

Восстановил сайт о Владимире Кобрине, человеке, которого на Западе называли концептуалистом, основателем русского авангарда в научном кино. Если вы не видели его работ - вы многое потеряли.
Если у вас есть дополнительная информация о Кобрине, сообщите мне. С миру по нитке.

05.02.10

Новый интерфейс и навигация Facebook

Последние пару месяцев мы тестировали несколько разных вариантов дизайна домашней страницы и собирали статистику по самым используемым компонентам страницы. Сегодня, мы начали выкладывать последние обновления интерфейса и навигации, чтобы помочь вам в поиске того, что вы ищете на Facebook'е. Теперь вы можете получить мгновенный доступ к новостям и важной информации с помощью обновленного верхнего меню и навигационного меню слева.

Оставайтесь в курсе событий с помощью верхнего меню

В верхнем меню вы найдете ваши последние уведомления, запросы и сообщения. К примеру, когда вы получите уведомление о том, что кто-то написал на вашей стене или отметил ваше фото - вы увидите как появится красный пузырек в левом углу, около строки поиска. Когда вы нажмете на эту иконку, вы увидите выпадающее меню со списком последний уведомлений.

image

Ссылки на "Профиль" (Profile) и "Домашнюю страницу" (Home) теперь расположены в верхнем правом углу, рядом с меню "Аккаунт" (Account), которое включает в себя настройки приватности и функцию выхода из аккаунта.

Исследуйте контент из левого меню

Левое меню теперь организовано так, чтобы упростить ваше общение с друзьями, а так же сделать удобной навигацию по контенту, добавленному вашими друзьями. Теперь вы можете получить доступ к сообщениям и другим основным функциям в одном месте, слева от вашей ленты новостей (News Feed).

С помощью панели фотографий (Photos dashboard) вы можете просматривать недавние фотографии ваших друзей. Панель событий (Events dashboard) отображает список ваших грядущих событий, вместе с событиями, на которые обратили внимание ваши друзья. Панель друзей (Friends dashboard) поможет вам найти друзей, увидеть какие из друзей недавно обновили свои профили, а так же фильтровать ленту новостей (News Feed) из списка друзей (Friend Lists).

image

Так же, мы сделали чат (Chat) в меню слева более заметным с помощью отображения списка некоторых ваших друзей, которые находятся онлайн. Этот список не полный, он включает в себя тех людей, с которыми вы общаетесь чаще всего. Чтобы увидеть весь список ваших друзей, которые находятся онлайн, вы можете открыть панель чата в нижнем правом углу или нажать "Показать всех" (See All) внизу вашего левого меню.

Взаимодействие с играми и приложениями

Вы упростили для вас поиск и взаимодействие с приложениями с новой панелью "Приложения и игры" (Applications and Games), доступной через ссылки "Приложения" (Applications) и "Игры" (Games) на домашней странице. Панель отображает приложения, которые вы используете чаще всего, а так же приложения, которые вы и ваши друзья недавно использовали.

Вы так же увидите счетчик приложений, которые вы добавили в закладки на своей домашней странице. Счетчики будут уведомлять вас о каких-либо действиях, которые вы должны выполнить, так что теперь вы никогда не пропустите свой ход в игре или какое-либо уведомление от друга в приложении.

Вы можете занести в закладки ваши любимые приложения используя кнопку "Добавить закладку" (Add Bookmark) в приложении, тем самым вы сможете перейти в приложение одним кликом из левого меню под панелью "Приложения и игры" (Applications and Games). Нажмите ссылку "Еще" (More), чтобы увидеть список всех ваших последних закладок.

image

На панеле "Приложения и игры" (Applications and Games) присутствует еще одна возможность получения персональных уведомлений из приложений. Новые уведомления будут появляться рядом с панелью приложений с таким текстом как "Почисти Stinky" (Clean Stinky) из приложения Pet Society или "Джо только что побил ваш рекорд" из Bejeweled Blitz.

Мы считаем, что обмен информацией о приложениях, которые вы используете, помогает вам и ваших друзьям обмениваться опытом. В тоже время, мы твердо уверены, что контроль является важным элементом при обмене информацией в Facebook. Именно поэтому эти новые возможности можно включить только с помощью настроек приватности (Privacy Settings).
Т.е. если вы не хотите, чтобы вашим друзьям были доступны последние новости и уведомления о ваших последних действиях в приложениях, вы всегда можете изменить эти настройки в свойствах приватности (Privacy Settings). Мы так же работает над более детальным контролем для отдельных приложений, так, чтобы вы могли включать и отключать эти функции для отдельных приложений, а не для всех сразу. В скором времени мы опубликуем дополнительную информацию по этому поводу.

Мы надеемся, что упрощенный дизайн вашей домашней странички поможет вам больше и проще общаться с людьми, использовать приложения и просматривать последние обновления, которые важны для вас. Чтобы узнать больше о новом дизайне, вы можете использовать тур по сайту.

Тут перевод заканчивается и начинает говорить переводчик.
Далеко не все сервера уже обновились, что значит новый интерфейс доступен еще не всем пользователям Facebook. Обещают обновить все сервера через пару дней. У меня тоже еще не обновился.
Как и водится, многим пришлось не по вкусе изменение привычного, поэтому Facebook кишит группами, типа "We Hate The New Facebook Design!!!". Но исходя из того, что я прочитал и что увидел, дизайн стал чуть лучше, а навигация и того похорошела. Ждем когда все обновится.
Целую. Всего хорошего!

04.02.10

Blogger разрешил создавать статические странички

Теперь вы можете добавить к блогу статические странички с описанием блога или портфолио, да с чем захотите.
Инструкция здесь.

02.02.10

Производительность автоматически сгенирированных методов доступа в Core Data

В Mac OS X 10.5, CoreData фреймворк перешел от использования метода valueForKey:, как рекомендованного способа доступа к атрибутам CoreData, к методу с автоматически генерируемыми методами доступа. Этот новый подход хорош для быстрого получения значений переменных, но проигрывает NSKeyValueCoding с его возможностью объединения значений, извлеченных из каждого объекта со связями "to-many", путем вызова одного метода.

В этой статье я рассмотрю возможность замены методов NSKeyValueCoding по обходу и объединению NSSet на вызовы автоматически сгенированных методов доступа к полям, чтобы сравнить производительность нового и старого методов.

Доступ к атрибутам и связям NSManagedObject


Давайте рассмотрим вопрос производительности в приложениях CoreData, используя следующую модель:

Если вы не знакомы с диаграммами CoreData, то важным моментом здесь является то, что каждая Company может иметь несколько Project и каждый Project может иметь несколько Employee.

С учетом этой модели, если у меня есть указатель, aCompany, который указывает на один из объектов Company, получить название компании довольно просто:
NSString *companyName = aCompany.name;

* This source code was highlighted with Source Code Highlighter.

Доступ к name здесь осуществляется с помощь автоматически генерируемых методов доступа, которые NSManagedObject предоставляет для нас.

До Mac OS X 10.5, единственным способом доступа к значениям полей в Core Data было использование метода Key Value Coding "ключ-значение" (key-value):
NSString *companyName = [aCompany valueForKey:@"name"];

* This source code was highlighted with Source Code Highlighter.

Так почему же решили уйти от использования метода "ключ-значение"? Основной причиной является производительность (хотя улучшился и синтаксис, и безопасность типов). Извлечение переменной миллион раз при использовании метода "ключ-значение" занимает 0.284016 секунд, а использование автоматически генерируемых методов доступа занимает 0.109017 секунд, что в 2,6 раза быстрее.

Обход значений


Но метод "ключ-значение" (старый метод) имеет одно важное преимущество перед автоматически сгенерированными методами: он очень быстр при обходе набора данных со связями "to-many".

Например, если я хочу получить полный набор имен Project используемых aCompany, то с Key Value Coding я могу сделать это очень легко:
NSSet *projectNames = [aCompany valueForKeyPath:@"projects.name"];

* This source code was highlighted with Source Code Highlighter.

Это сработает, потому что реализация NSSet из NSKeyValueCoding умеет автоматически обходить себя, чтобы получить имена для каждого объекта Project, который она содержит.

Эквивалентом с использованием методов доступа было бы:
NSMutableSet *result = [NSMutableSet set];
for (Project *project in aCompany.projects)
{
  NSString *name = project.name;
  if (value)
  {
    [result addObject:value];
  }
}


* This source code was highlighted with Source Code Highlighter.

Нам потребовалось не только написать больше кода, чем при использовании метода "ключ-значение", но и этот метод действительно медленнее. Для 10000 объектов Company, каждый из которых содержит 100 объектов Project, обход с использованием метода "ключ-значение" займет 0,25692 секунды, а подход, с использованием автоматически сгенерированных методово доступа, занимает 0,52873 секунды.

Новый и усовершенствованный подход прошел свой путь от "в 2,6 раза быстрее", в случае с доступом к полю, к "2 раза медленнее", в случае с обходом множества полей приведенным выше.

Исправление проблем со скоростью


Старый метод все еще быстрее

Прежде чем я объясню, почему новый метод медленнее, важно понять, что метод "ключ-значение" на самом деле быстрее при использовании обхода набора данных. Несмотря на дополнительную работу, связанную с обходом полей Company в Project и помещением значений в NSSet, метод "ключ-значение" потратил лишь 0,25692 секунды, что извлечь один миллион имен из Project, по сравнению с 0,284016 секунд затраченных на извлечение одного миллиона имен Company.

Это не глюк, и, несмотря на больший объем работы, при использовании метода "ключ-значение" производительность заметно увеличивается при множестве внутренних итераций (в рамках пути по ключу), а не извне (как я делал при переборе более одного миллиона объектов Company).

Несмотря на заметные улучшения, мы должны таки побить Key Value Coding, используя наш подход с автоматически генерируемыми методами доступа, но разница в производительности будет незначительно менее эффективной, чем это было при итерации aCompany.name...

Исправление проблем с новым методом


Простой профайлинг быстро обнаруживает, что имеющиеся здесь проблемы имеют мало общего с методами доступа к объектам. Медленная скорость в первую очередь объясняется вызовом addObject:.

Посмотрев на вызов private методов стека в профайлере, стало ясно, что причиной всех проблем является перераспределение данных. Каждый раз, когда NSMutableSet необходимо увеличится в размерах, происходит перераспределение памяти, что и приводит к низкой производительности.

Мы можем предварительно выделить память для всего набора данных на основе наибольшего размера (подразумевая, что все имена Project уникальны). Наш код становится таким:
NSSet *projects = aCompany.projects;
NSMutableSet *result = [NSMutableSet setWithCapacity:[projects count]];
for (Project *project in projects)
{
  NSString *name = project.name;
  if (value)
  {
    [result addObject:value];
  }
}


* This source code was highlighted with Source Code Highlighter.

Победа! Эта версия работает за 0,19104 секунд (по сравнению с полученными ранее 0,52873 секунд), что на 25% быстрее, чем подход с использованием метода "ключ-значение".

Уже не в 2,6 раза быстрее, но реализация Key Value Coding в NSSet имеет ряд преимуществ: поскольку имеется внутренний доступ к хранилищу, NSSet может оптимизировать итерации со связами "to-many" и, соответственно, может создавать новые наборы данных быстрее, чем мы.

Реализация категории


(Category (категория). Objective-C позволяет очень просто расширять функциональность имеющихся классов. Он поддерживает так называемые категории, которые позволяют модифицировать существующие классы «на месте». С помощью категорий можно добавить требуемую функциональность, не внося в них изменений и даже вообще не имея доступа к исходному коду существующих классов).
Для повторного использования этого подхода в будущем, мы можем реализовать категории для NSSet.

Там будет два метода:

* objectValuesForProperty:
* coalescedValuesForProperty:

Первый будет осуществлять упомянутое в примере выше (где NSSet содержит основные объекты).

Второй будет реплицировать оператор Key Value Coding @distinctUnionOfSets (обрабатывать случай, когда NSSet содержит NSSet и нужно объединять объекты внутри множества).

Примером второго метода будет получение всех объектов Employee в Company. В случае использования Key Value Coding мы должны написать:
NSSet *allEmployees = [aCompany valueForKeyPath:@"projects.@distinctUnionOfSets.employees"];

* This source code was highlighted with Source Code Highlighter.

С методом coalescedValuesForProperty:, мы можем написать:
NSSet *allEmployees = [aCompany.projects coalescedValuesForProperty:@selector(employees)];

* This source code was highlighted with Source Code Highlighter.

Реализация:
#import <objc/message.h>

@implementation NSSet (PropertyCoalescing)

- (NSSet *)objectValuesForProperty:(SEL)propertySelector
{
  NSMutableSet *result = [NSMutableSet setWithCapacity:[self count]];
  for (id object in self)
  {
    id value = objc_msgSend(object, propertySelector);
    if (value)
    {
      [result addObject:value];
    }
  }
  return result;
}

- (NSSet *)coalescedValuesForProperty:(SEL)propertySelector
{
  NSInteger count = 0;
  for (id object in self)
  {
    count += [objc_msgSend(object, propertySelector) count];
  }
  NSMutableSet *result = [NSMutableSet setWithCapacity:count];
  for (id object in self)
  {
    id value = objc_msgSend(object, propertySelector);
    if (value)
    {
      [result unionSet:value];
    }
  }
  return result;
}

@end


* This source code was highlighted with Source Code Highlighter.

С методом coalescedValuesForProperty: мы перебираем набор данных дважды, чтобы получить размер, но это по-прежнему самый быстрый вариант - на самом деле, этот метод примерно на 35% быстрее, чем при использовании Key Value Coding. В сравнении с objectValuesForProperty: прирост производительности составит 25%.

Заключение


По просьбе читателей, вот код, который я использовал в тестировании: PropertyAccessors.zip (32kB). Это наспех написанный код для этой заметки, так что код не очень хорошо написан, но он есть, если он вам нужен.

Я написал этот код и провел тесты производительности, потому что у меня много кода, который использует Key Value Coding для обхода данных с отношениями "to-many". Я был обеспокоен тем, что CoreData предлагает использовать автоматически генерируемые методы доступа из соображений производительности и мои методы с использованием "ключ-значение" будут значительно медленнее в этих случаях, чем следовало бы.

Результатом является то, что производительность Key Value Coding для обхода набора данных в CoreData дает прирост производительности лишь 25-35%, а не на 260% в сравнении с заменой Key Value Coding на использование индивидуального доступа к свойствам объекта. Key Value Coding достаточно эффективен при работе с наборами данных - безусловно, более эффективен, чем доступ к уникальным полям.

Безусловно, улучшение производительности на 35% будет полезным в критически важных участках кода.

Что касается непосредственно реализации: не стоит недооценивать влияния на производительность фактора перераспределения памяти. Постоянный растущий с помощью addObject: NSSet работает в 3 раза медленнее, чем если бы вы выделили память на весь набор данных сразу.

Удобно заранее выделить память для NSMutableSet, чтобы вместить все объекты, но, если не все объекты будут уникальными, то вы выделите больше памяти, чем требуется. Если лишняя трата памяти имеет для вас значение, вы можете скопировать множество в момент его создания. Копия будет такого размера, как вам требуется и вы сможете удалить из памяти оригинал. Недостатком такого меода является то, что процесс копирования добавит еще 10-15% ко времени исполнения.

01.02.10

потоки сознания в Java

люблю когда if (true && false)

public class True {

static boolean aha;

public static void main(String[] argc) {
new Thread() {
public void run() {
while (true) {
aha = !aha;
}
}
}.start();
while (true) {
if (aha && !aha) {
System.out.println("AHAA!!! " + aha + ":" + aha);
}
}
}
}

30.01.10

Twitter Hotkeys

С удивлением обнаружил, что в веб интерфейсе Твиттера работают горячие клавиши (во всяком случае в Safari под Mac'ом). Стало в разы удобнее.


PDF тут.