Почему объект System.String не может кэшировать свой хэш-код?

Решение, предложенное ниже ответа, не работает с Nimbus Look and Feel. Нимбус переопределяет цвет фона и делает фон белым. Решение состоит в том, чтобы установить цвет фона в css. Вам также необходимо удалить границу компонента. Вот класс, который реализует решение, которое работает с нимбом (я не проверял другие L & amp; F):

import java.awt.Color;
import java.awt.Font;

import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;

public class MessageWithLink extends JEditorPane {
    private static final long serialVersionUID = 1L;

    public MessageWithLink(String htmlBody) {
        super("text/html", "<html><body style=\"" + getStyle() + "\">" + htmlBody + "</body></html>");
        addHyperlinkListener(new HyperlinkListener() {
            @Override
            public void hyperlinkUpdate(HyperlinkEvent e) {
                if (e.getEventType().equals(HyperlinkEvent.EventType.ACTIVATED)) {
                    // Process the click event on the link (for example with java.awt.Desktop.getDesktop().browse())
                    System.out.println(e.getURL()+" was clicked");
                }
            }
        });
        setEditable(false);
        setBorder(null);
    }

    static StringBuffer getStyle() {
        // for copying style
        JLabel label = new JLabel();
        Font font = label.getFont();
        Color color = label.getBackground();

        // create some css from the label's font
        StringBuffer style = new StringBuffer("font-family:" + font.getFamily() + ";");
        style.append("font-weight:" + (font.isBold() ? "bold" : "normal") + ";");
        style.append("font-size:" + font.getSize() + "pt;");
        style.append("background-color: rgb("+color.getRed()+","+color.getGreen()+","+color.getBlue()+");");
        return style;
    }
}

Использование:

JOptionPane.showMessageDialog(null, new MessageWithLink("Here is a link on <a href=\"http://www.google.com\">http://www.google.com</a>"));
38
задан Dan Tao 16 June 2010 в 14:51
поделиться

4 ответа

Очевидный потенциальный ответ: потому что это будет стоить памяти.

Здесь есть анализ затрат/выгод:

Затраты: 4 байта для каждой строки (и быстрый тест при каждом вызове GetHashCode). Также сделайте объект строки изменяемым, что, очевидно, означает, что вам нужно быть осторожным с реализации - если только вы не будете всегда вычислять хэш-код заранее, что будет стоить вычисления один раз для каждой строки, независимо от того, хэшируете ли вы ее вообще.

Benefit: Избегать повторного вычисления хэша для строковых значений, хэшированных более одного раза

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

Я не думаю, что нахожусь в хорошем положении, чтобы судить, что встречается чаще... Я бы надеялся, что MS проинструктировала различные реальные приложения. (Я также надеюсь, что Sun сделала то же самое для Java, которая действительно кэширует хэш...)

EDIT: Я только что говорил об этом с Эриком Липпертом (NDC - это круто :) и в основном это это о дополнительном ударе по памяти против ограниченных преимуществ.

28
ответ дан 27 November 2019 в 02:45
поделиться

Во-первых, неизвестно, улучшит ли кеширование этот результат Dictionary и др., Потому что они не обязательно используют String.GetHashCode, потому что он использует IComparer для получения хэш-кода для строки.

И если вы проследите вероятную цепочку вызовов для класса StringComparer, он перейдет к классу System.Globalization.CompareInfo, который, наконец, завершится этим методом:

[SecurityCritical, SuppressUnmanagedCodeSecurity, DllImport("QCall",
   CharSet=CharSet.Unicode)]
private static extern int InternalGetGlobalizedHashCode(IntPtr handle, string
   localeName, string source, int length, int dwFlags);

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

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

Таким образом, хотя дополнительная память может быть фактором , я на самом деле думаю, что это потому, что хранение хеш-кода вместе с экземпляром строки вводит в заблуждение вызывающего, и действительно.Чистая внутренняя команда разработчиков (!), Думая, что строка имеет только один хеш-код, хотя на самом деле это полностью зависит от того, как вы собираетесь ее интерпретировать - как серию байтов (чего у большинства из нас нет) или как серию печатных символов.

С точки зрения производительности, тогда, если мы также согласимся с тем, что эти компараторы, используемые Dictionary <,> и т. Д., Не могут использовать внутреннюю реализацию, без кеширования этот результат, вероятно, не имеет большое влияние, потому что, откровенно говоря, как часто этот метод будет фактически вызываться в реальном мире: поскольку большую часть времени хэш-код строки, скорее всего, вычисляется через какой-то другой механизм.

РЕДАКТИРОВАТЬ

В ответе Тима также есть точка зрения (+1 там). Если он прав, а я думаю, что он прав, то нет никакой гарантии, что строка действительно остается неизменной после построения, поэтому кешировать результат было бы неправильно.

ДОПОЛНИТЕЛЬНОЕ РЕДАКТИРОВАНИЕ (!)

Дэн подчеркивает, что строки должны быть неизменяемыми в сфере Net, и поэтому эта строка должна быть свободной для кэширования своего собственного хэш-кода на основе этого. Проблема здесь в том, что инфраструктура .Net также предоставляет законный способ изменения предположительно неизменяемой строки , который не требует привилегированного отражения или чего-либо еще. Это фундаментальная проблема со строками, это указатель на буфер, которым вы не можете управлять. Неважно в мире C #, как насчет C ++, где векторизация и изменение буферов памяти являются обычным делом. То, что вы в идеале не должны делать это, не означает, что фреймворк должен ожидать, что вы этого не сделаете.

.Net обеспечивает эту функциональность, и поэтому, если это было дизайнерское решение команды .Net в ответ на вид бинарного бандитизма, предложенный Тимом, то они были очень мудры, чтобы принять это во внимание. Сделали ли они это, или это случайно, совсем другой вопрос! :)

13
ответ дан 27 November 2019 в 02:45
поделиться

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

Но, как отмечали другие, основная причина отказа от кэширования значения заключается в том, что использование дополнительной памяти, вероятно, намного перевешивает потенциальную экономию от кэширования хэш-кода. Время выполнения GetHashCode составляет O (N) на длине строки, поэтому наихудший сценарий повторного хеширования хорошо ограничен.

1
ответ дан 27 November 2019 в 02:45
поделиться

Возможно, я сделал неправильный вывод, но разве не правда, что хотя строка неизменяема в контексте объекта .NET String, значение все же можно изменить?

Например, если бы вы захотели сделать это...

String example = "Hello World";

unsafe
{
    fixed (char* strPointer = myString) {
        strPointer[1] = 'a';
    }
} 

... не будет ли example по-прежнему представлять тот же объект String, но теперь со значением, которое будет вычислять другое значение для GetHashCode()? Возможно, я не совсем прав, но поскольку вы можете легко (если не бессмысленно) сделать это, это также вызовет некоторые проблемы.

12
ответ дан 27 November 2019 в 02:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: