Tengo una aplicación heredada que toma un número entero, lo convierte en una cadena binaria, invierte esa cadena y luego obtiene las posiciones de los bits (unos) como una lista de números enteros. Por ejemplo:
6 -> "110" -> "011" -> (2,3) 7 -> "111" -> "111" -> (1,2,3) 8 -> "1000" -> "0001" -> (4) ¿Cuál es una forma sucinta y clara de lograr esto en Java moderno sin las operaciones de cadena? La conversión hacia y desde String me parece un desperdicio, y sé que de todos modos no hay una manera simple de voltear una Cadena (no String.reverse() ).
No necesita invertir la cadena binaria real. Puedes simplemente calcular el índice.
String str = Integer.toBinaryString(num); int len = str.length(); List<Integer> list = new ArrayList<>(); for (int i=0; i < len; i ++) { if (str.charAt(i) == '1') list.add(len - 1 - i); }Simplemente puede probar los bits sin convertir el número entero en una cadena:
List<Integer> onePositions(int input) { List<Integer> onePositions = new ArrayList<>(); for (int bit = 0; bit < 32; bit++) { if (input & (1 << bit) != 0) { onePositions.add(bit + 1); // One-based, for better or worse. } } return onePositions; } Los bits generalmente se cuentan de derecha a izquierda, siendo el bit más a la derecha el bit 0. La operación 1 << bit le da un int cuyo bit numerado se establece en 1 (y el resto en 0). Luego use & (y binario) para verificar si este bit está establecido en la input y, de ser así, registre la posición en la matriz de salida.
Simplemente verifique los bits a su vez:
List<Integer> bits(int num) { List<Integer> setBits = new ArrayList<>(); for (int i = 1; num != 0; ++i, num >>>= 1) { if ((num & 1) != 0) setBits.add(i); } return setBits; } 6 [2, 3] 7 [1, 2, 3] 8 [4]Simplemente use la función indexOf de la clase String
public class TestPosition { public static void main(String[] args) { String word = "110"; // your string String guess = "1"; // since we are looking for 1 int totalLength = word.length(); int index = word.indexOf(guess); while (index >= 0) { System.out.println(totalLength - index); index = word.indexOf(guess, index + 1); } } }Puedes usar esta solución:
static List<Integer> convert(int input) { List<Integer> list = new ArrayList<>(); int counter = 1; int num = (input >= 0) ? input : Integer.MAX_VALUE + input + 1; while (num > 0) { if (num % 2 != 0) { list.add(counter); } ++counter; num /= 2; } return list; }Produce:
[2, 3] [1, 2, 3] [4]Definitivamente preferiría la respuesta de Andy, incluso si parece críptica al principio. Pero como nadie aquí tiene una respuesta con las transmisiones todavía (incluso si están totalmente fuera de lugar aquí):
public List<Integer> getList(int x) { String str = Integer.toBinaryString(x); final String reversed = new StringBuilder(str).reverse().toString(); return IntStream.range(1, str.length()+1) .filter(i -> reversed.charAt(i-1)=='1') .boxed() .collect(Collectors.toList()); }Como escribió "Java moderno", así es como se puede hacer con flujos (Java 8 o superior):
final int num = 7; List<Integer> digits = IntStream.range(0,31).filter(i-> ((num & 1<<i) != 0)) .map(i -> i+1).boxed().collect(Collectors.toList());El mapa solo es necesario ya que empiezas a contar desde 1 y no desde 0.
Luego
System.out.println(digits);huellas dactilares
[1, 2, 3]Una respuesta tonta, solo por variedad:
BitSet bs = BitSet.valueOf(new long[] {0xFFFFFFFFL & input}); List<Integer> setBits = new ArrayList<>(); for (int next = -1; (next = bs.nextSetBit(next + 1)) != -1;) { setBits.add(next + 1); }(Gracias a pero_hero por señalar que el enmascaramiento era necesario en la respuesta de WJS)
solo por diversión:
Pattern one = Pattern.compile("1"); List<Integer> collect = one.matcher( new StringBuilder(Integer.toBinaryString(value)).reverse()) .results() .map(m -> m.start() + 1) .collect(Collectors.toList()); System.out.println(collect);Dado el entero original, devuelve una lista con las posiciones de los bits.
static List<Integer> bitPositions(int v) { return BitSet.valueOf(new long[]{v&0xFF_FF_FF_FFL}) .stream() .mapToObj(b->b+1) .collect(Collectors.toList()); }O si quieres hacer cambios de bits.
static List<Integer> bitPositions(int v ) { List<Integer> bits = new ArrayList<>(); int pos = 1; while (v != 0) { if ((v & 1) == 1) { bits.add(pos); } pos++; v >>>= 1; } return bits; }o si quieres:
String strValue = Integer.toBinaryString(value); List<Integer> collect2 = strValue.codePoints() .collect(ArrayList<Integer>::new, (l, v) -> l.add(v == '1' ? strValue.length() - l.size() : -1), (l1, l2) -> l1.addAll(l2)).stream() .filter(e -> e >= 0) .sorted() .collect(toList());¿Puedo proponer una solución bit-wise pura?
static List<Integer> onesPositions(int input) { List<Integer> result = new ArrayList<Integer>(Integer.bitCount(input)); while (input != 0) { int one = Integer.lowestOneBit(input); input = input - one; result.add(Integer.numberOfTrailingZeros(one)); } return result; }Esta solución es algorítmicamente óptima:
Integer.bitCount para dimensionar apropiadamente ArrayList por adelantado.El bucle interno es bastante simple:
Integer.lowestOneBit devuelve un int con solo el bit más bajo del conjunto de entrada.input - one "desestablece" este bit de la entrada, para la próxima iteración.Integer.numberOfTrailingZeros cuenta el número de ceros finales, en binario, dándonos efectivamente el índice del bit más bajo. 1 Es notable que esta puede no ser la forma más óptima una vez compilada y que, en cambio, un bucle explícito 0..n basado en bitCount sería más fácil de desarrollar para el JIT.
una versión corriente de @Matthieu M. respuesta:
List<Integer> list = IntStream.iterate(value, (v) -> v != 0, (v) -> v & (v - 1)) .mapToObj(val -> Integer.numberOfTrailingZeros(val) + 1) .collect(toList());